iPXE
image.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25FILE_SECBOOT ( PERMITTED );
26
27#include <stddef.h>
28#include <string.h>
29#include <stdlib.h>
30#include <stdio.h>
31#include <ctype.h>
32#include <errno.h>
33#include <assert.h>
34#include <libgen.h>
35#include <syslog.h>
36#include <ipxe/list.h>
37#include <ipxe/uaccess.h>
38#include <ipxe/umalloc.h>
39#include <ipxe/uri.h>
40#include <ipxe/image.h>
41
42/** @file
43 *
44 * Executable images
45 *
46 */
47
48/* Disambiguate the various error causes */
49#define EACCES_UNTRUSTED \
50 __einfo_error ( EINFO_EACCES_UNTRUSTED )
51#define EINFO_EACCES_UNTRUSTED \
52 __einfo_uniqify ( EINFO_EACCES, 0x01, "Untrusted image" )
53#define EACCES_PERMANENT \
54 __einfo_error ( EINFO_EACCES_PERMANENT )
55#define EINFO_EACCES_PERMANENT \
56 __einfo_uniqify ( EINFO_EACCES, 0x02, "Trust requirement is permanent" )
57
58/** List of registered images */
60
61/** Image selected for execution */
63 .name = "SELECTED",
64};
65
66/** Currently-executing image */
68 .name = "CURRENT",
69};
70
71/** Current image trust requirement */
73
74/** Prevent changes to image trust requirement */
76
77/**
78 * Free executable image
79 *
80 * @v refcnt Reference counter
81 *
82 * Image consumers must call image_put() rather than calling
83 * free_image() directly. This function is exposed for use only by
84 * static images.
85 */
86void free_image ( struct refcnt *refcnt ) {
87 struct image *image = container_of ( refcnt, struct image, refcnt );
88 struct image_tag *tag;
89
90 /* Sanity check: free_image() should not be called directly on
91 * dynamically allocated images.
92 */
93 assert ( refcnt->count < 0 );
94 DBGC ( image, "IMAGE %s freed\n", image->name );
95
96 /* Clear any tag weak references */
98 if ( tag->image == image )
99 tag->image = NULL;
100 }
101
102 /* Free dynamic allocations used by both static and dynamic images */
103 free ( image->cmdline );
104 uri_put ( image->uri );
106
107 /* Free image name, if dynamically allocated */
108 if ( ! ( image->flags & IMAGE_STATIC_NAME ) )
109 free ( image->name );
110
111 /* Free image data and image itself, if dynamically allocated */
112 if ( ! ( image->flags & IMAGE_STATIC ) ) {
113 ufree ( image->rwdata );
114 free ( image );
115 }
116}
117
118/**
119 * Allocate executable image
120 *
121 * @v uri URI, or NULL
122 * @ret image Executable image
123 */
124struct image * alloc_image ( struct uri *uri ) {
125 struct image *image;
126 int rc;
127
128 /* Allocate image */
129 image = zalloc ( sizeof ( *image ) );
130 if ( ! image )
131 goto err_alloc;
132
133 /* Initialise image */
135 if ( uri && ( ( rc = image_set_uri ( image, uri ) ) != 0 ) )
136 goto err_set_uri;
137
138 return image;
139
140 err_set_uri:
141 image_put ( image );
142 err_alloc:
143 return NULL;
144}
145
146/**
147 * Set image URI
148 *
149 * @v image Image
150 * @v uri New image URI
151 * @ret rc Return status code
152 */
153int image_set_uri ( struct image *image, struct uri *uri ) {
154 const char *name;
155 int rc;
156
157 /* Set name, if image does not already have one */
158 if ( ! ( image->name && image->name[0] ) ) {
159 name = ( uri->path ? uri->path : uri->opaque );
160 if ( name ) {
161 name = basename ( ( char * ) name );
162 if ( ( rc = image_set_name ( image, name ) ) != 0 )
163 return rc;
164 }
165 }
166
167 /* Update image URI */
168 uri_put ( image->uri );
169 image->uri = uri_get ( uri );
170
171 return 0;
172}
173
174/**
175 * Set image name
176 *
177 * @v image Image
178 * @v name New image name
179 * @ret rc Return status code
180 */
181int image_set_name ( struct image *image, const char *name ) {
182 char *name_copy;
183
184 /* Duplicate name */
185 name_copy = strdup ( name );
186 if ( ! name_copy )
187 return -ENOMEM;
188
189 /* Free existing name, if not statically allocated */
190 if ( ! ( image->flags & IMAGE_STATIC_NAME ) )
191 free ( image->name );
192
193 /* Replace existing name */
194 image->name = name_copy;
196
197 return 0;
198}
199
200/**
201 * Strip dot suffix from image name, if present
202 *
203 * @v image Image
204 * @ret sep Position of old dot separator, or NULL
205 */
206char * image_strip_suffix ( struct image *image ) {
207 char *dot;
208
209 /* Locate and strip suffix, if present */
210 if ( image->name &&
211 ( ( dot = strrchr ( image->name, '.' ) ) != NULL ) ) {
212 *dot = '\0';
213 return dot;
214 }
215
216 return NULL;
217}
218
219/**
220 * Set image command line
221 *
222 * @v image Image
223 * @v cmdline New image command line, or NULL
224 * @ret rc Return status code
225 */
226int image_set_cmdline ( struct image *image, const char *cmdline ) {
227
228 free ( image->cmdline );
229 image->cmdline = NULL;
230 if ( cmdline ) {
232 if ( ! image->cmdline )
233 return -ENOMEM;
234 }
235 return 0;
236}
237
238/**
239 * Set image length
240 *
241 * @v image Image
242 * @v len Length of image data
243 * @ret rc Return status code
244 */
245int image_set_len ( struct image *image, size_t len ) {
246 void *new;
247
248 /* Refuse to reallocate static images */
249 if ( image->flags & IMAGE_STATIC )
250 return -ENOTTY;
251
252 /* (Re)allocate image data */
253 new = urealloc ( image->rwdata, len );
254 if ( ! new )
255 return -ENOMEM;
256 image->rwdata = new;
257 image->len = len;
258
259 return 0;
260}
261
262/**
263 * Set image data
264 *
265 * @v image Image
266 * @v data Image data
267 * @v len Length of image data
268 * @ret rc Return status code
269 */
270int image_set_data ( struct image *image, const void *data, size_t len ) {
271 int rc;
272
273 /* Set image length */
274 if ( ( rc = image_set_len ( image, len ) ) != 0 )
275 return rc;
276
277 /* Copy in new image data */
278 memcpy ( image->rwdata, data, len );
279
280 return 0;
281}
282
283/**
284 * Determine image type
285 *
286 * @v image Executable image
287 * @ret rc Return status code
288 */
289static int image_probe ( struct image *image ) {
290 struct image_type *type;
291 int rc;
292
293 /* Try each type in turn */
295 if ( ( rc = type->probe ( image ) ) == 0 ) {
296 image->type = type;
297 DBGC ( image, "IMAGE %s is %s\n",
298 image->name, type->name );
299 return 0;
300 }
301 DBGC ( image, "IMAGE %s is not %s: %s\n", image->name,
302 type->name, strerror ( rc ) );
303 }
304
305 DBGC ( image, "IMAGE %s format not recognised\n", image->name );
306 return -ENOTSUP;
307}
308
309/**
310 * Register executable image
311 *
312 * @v image Executable image
313 * @ret rc Return status code
314 */
315int register_image ( struct image *image ) {
316 static unsigned int imgindex = 0;
317 char name[8]; /* "imgXXXX" */
318 int rc;
319
320 /* Sanity checks */
321 if ( image->flags & IMAGE_STATIC ) {
322 assert ( ( image->name == NULL ) ||
324 assert ( image->cmdline == NULL );
325 }
326
327 /* Create image name if it doesn't already have one */
328 if ( ! image->name ) {
329 snprintf ( name, sizeof ( name ), "img%d", imgindex++ );
330 if ( ( rc = image_set_name ( image, name ) ) != 0 )
331 return rc;
332 }
333
334 /* Add to image list */
335 image_get ( image );
338 DBGC ( image, "IMAGE %s at [%lx,%lx) registered\n",
339 image->name, virt_to_phys ( image->data ),
340 ( virt_to_phys ( image->data ) + image->len ) );
341
342 /* Try to detect image type, if applicable. Ignore failures,
343 * since we expect to handle some unrecognised images
344 * (e.g. kernel initrds, multiboot modules, random files
345 * provided via our EFI virtual filesystem, etc).
346 */
347 if ( ! image->type )
348 image_probe ( image );
349
350 return 0;
351}
352
353/**
354 * Unregister executable image
355 *
356 * @v image Executable image
357 */
358void unregister_image ( struct image *image ) {
359
360 /* Do nothing unless image is registered */
361 if ( ! ( image->flags & IMAGE_REGISTERED ) )
362 return;
363
364 DBGC ( image, "IMAGE %s unregistered\n", image->name );
365 list_del ( &image->list );
367 image_put ( image );
368}
369
370/**
371 * Find image by name
372 *
373 * @v name Image name
374 * @ret image Executable image, or NULL
375 */
376struct image * find_image ( const char *name ) {
377 struct image *image;
378
380 if ( strcmp ( image->name, name ) == 0 )
381 return image;
382 }
383
384 return NULL;
385}
386
387/**
388 * Find image by tag
389 *
390 * @v tag Image tag
391 * @ret image Executable image, or NULL
392 */
393struct image * find_image_tag ( struct image_tag *tag ) {
394 struct image *image;
395
397 if ( tag->image == image )
398 return image;
399 }
400
401 return NULL;
402}
403
404/**
405 * Execute image
406 *
407 * @v image Executable image
408 * @ret rc Return status code
409 *
410 * The image must already be registered. Note that executing an image
411 * may cause it to unregister itself. The caller must therefore
412 * assume that the image pointer becomes invalid.
413 */
414int image_exec ( struct image *image ) {
415 struct image *saved_current_image;
416 struct image *replacement = NULL;
417 struct uri *old_cwuri;
418 int rc;
419
420 /* Sanity check */
422
423 /* Switch current working directory to be that of the image
424 * itself, if applicable
425 */
426 old_cwuri = uri_get ( cwuri );
427 if ( image->uri )
428 churi ( image->uri );
429
430 /* Set as currently running image */
431 saved_current_image = image_tag ( image, &current_image );
432
433 /* Take out a temporary reference to the image, so that it
434 * does not get freed when temporarily unregistered.
435 */
436 image_get ( image );
437
438 /* Check that this image can be executed */
439 if ( ! ( image->type && image->type->exec ) ) {
440 rc = -ENOEXEC;
441 goto err;
442 }
443
444 /* Check that image is trusted (if applicable) */
446 DBGC ( image, "IMAGE %s is not trusted\n", image->name );
448 goto err;
449 }
450
451 /* Record boot attempt */
452 syslog ( LOG_NOTICE, "Executing \"%s\"\n", image->name );
453
454 /* Temporarily unregister the image during its execution */
456
457 /* Try executing the image */
458 if ( ( rc = image->type->exec ( image ) ) != 0 ) {
459 DBGC ( image, "IMAGE %s could not execute: %s\n",
460 image->name, strerror ( rc ) );
461 /* Do not return yet; we still have clean-up to do */
462 }
463
464 /* Record result of boot attempt */
465 if ( rc == 0 ) {
466 syslog ( LOG_NOTICE, "Execution of \"%s\" completed\n",
467 image->name );
468 } else {
469 syslog ( LOG_ERR, "Execution of \"%s\" failed: %s\n",
470 image->name, strerror ( rc ) );
471 }
472
473 /* Re-register image (unless due to be replaced) */
474 if ( ! image->replacement )
476
477 /* Pick up replacement image before we drop the original
478 * image's temporary reference. The replacement image must
479 * already be registered, so we don't need to hold a temporary
480 * reference (which would complicate the tail-recursion).
481 */
483 if ( replacement )
485
486 /* Clear any recorded replacement image */
489
490 err:
491 /* Unregister image if applicable */
494
495 /* Debug message for tail-recursion. Placed here because the
496 * image_put() may end up freeing the image.
497 */
498 if ( replacement ) {
499 DBGC ( image, "IMAGE %s replacing self with IMAGE %s\n",
500 image->name, replacement->name );
501 }
502
503 /* Drop temporary reference to the original image */
504 image_put ( image );
505
506 /* Restore previous currently-running image */
507 image_tag ( saved_current_image, &current_image );
508
509 /* Reset current working directory */
510 churi ( old_cwuri );
511 uri_put ( old_cwuri );
512
513 /* Tail-recurse into replacement image, if one exists */
514 if ( replacement )
515 return image_exec ( replacement );
516
517 return rc;
518}
519
520/**
521 * Set replacement image
522 *
523 * @v replacement Replacement image
524 * @ret rc Return status code
525 *
526 * The replacement image must already be registered, and must remain
527 * registered until the currently-executing image returns.
528 */
530 struct image *image = current_image.image;
531 int rc;
532
533 /* Sanity check */
535
536 /* Fail unless there is a currently-executing image */
537 if ( ! image ) {
538 rc = -ENOTTY;
539 DBGC ( replacement, "IMAGE %s cannot replace non-existent "
540 "image: %s\n", replacement->name, strerror ( rc ) );
541 return rc;
542 }
543
544 /* Check that the replacement image can be executed */
545 if ( ! ( replacement->type && replacement->type->exec ) )
546 return -ENOEXEC;
547
548 /* Clear any existing replacement */
550
551 /* Set replacement */
553 DBGC ( image, "IMAGE %s will replace self with IMAGE %s\n",
554 image->name, replacement->name );
555
556 return 0;
557}
558
559/**
560 * Select image for execution
561 *
562 * @v image Executable image
563 * @ret rc Return status code
564 */
565int image_select ( struct image *image ) {
566
567 /* Check that this image can be executed */
568 if ( ! ( image->type && image->type->exec ) )
569 return -ENOEXEC;
570
571 /* Mark image as selected */
573
574 return 0;
575}
576
577/**
578 * Change image trust requirement
579 *
580 * @v require_trusted Require trusted images
581 * @v permanent Make trust requirement permanent
582 * @ret rc Return status code
583 */
584int image_set_trust ( int require_trusted, int permanent ) {
585
586 /* Update trust requirement, if permitted to do so */
588 require_trusted_images = require_trusted;
590 }
591
592 /* Fail if we attempted to change the trust requirement but
593 * were not permitted to do so.
594 */
595 if ( require_trusted_images != require_trusted )
596 return -EACCES_PERMANENT;
597
598 return 0;
599}
600
601/**
602 * Create registered image from block of memory
603 *
604 * @v name Name
605 * @v data Image data
606 * @v len Length
607 * @ret image Image, or NULL on error
608 */
609struct image * image_memory ( const char *name, const void *data,
610 size_t len ) {
611 struct image *image;
612 int rc;
613
614 /* Allocate image */
615 image = alloc_image ( NULL );
616 if ( ! image ) {
617 rc = -ENOMEM;
618 goto err_alloc_image;
619 }
620
621 /* Set name */
622 if ( ( rc = image_set_name ( image, name ) ) != 0 )
623 goto err_set_name;
624
625 /* Set data */
626 if ( ( rc = image_set_data ( image, data, len ) ) != 0 )
627 goto err_set_data;
628
629 /* Register image */
630 if ( ( rc = register_image ( image ) ) != 0 )
631 goto err_register;
632
633 /* Drop local reference to image */
634 image_put ( image );
635
636 return image;
637
638 err_register:
639 err_set_data:
640 err_set_name:
641 image_put ( image );
642 err_alloc_image:
643 return NULL;
644}
645
646/**
647 * Find argument within image command line
648 *
649 * @v image Image
650 * @v key Argument search key (including trailing delimiter)
651 * @ret value Argument value, or NULL if not found
652 */
653const char * image_argument ( struct image *image, const char *key ) {
654 const char *cmdline = image->cmdline;
655 const char *search;
656 const char *match;
657 const char *next;
658
659 /* Find argument */
660 for ( search = cmdline ; search ; search = next ) {
661
662 /* Find next occurrence, if any */
663 match = strstr ( search, key );
664 if ( ! match )
665 break;
666 next = ( match + strlen ( key ) );
667
668 /* Check preceding delimiter, if any */
669 if ( ( match == cmdline ) || isspace ( match[-1] ) )
670 return next;
671 }
672
673 return NULL;
674}
#define NULL
NULL pointer (VOID *)
Definition Base.h:322
union @162305117151260234136356364136041353210355154177 key
Sense key.
Definition scsi.h:3
struct arbelprm_rc_send_wqe rc
Definition arbel.h:3
Assertions.
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:50
const char * name
Definition ath9k_hw.c:1986
char * basename(char *path)
Return base name from path.
Definition basename.c:43
int isspace(int character)
Check to see if character is a space.
Definition ctype.c:42
Character types.
void churi(struct uri *uri)
Change working URI.
Definition cwuri.c:46
struct uri * cwuri
Current working URI.
Definition cwuri.c:39
uint32_t next
Next descriptor address.
Definition dwmac.h:11
ring len
Length.
Definition dwmac.h:226
uint64_t tag
Identity tag.
Definition edd.h:1
const char * replacement
Definition editstring.h:54
uint32_t type
Operating system type.
Definition ena.h:1
uint8_t data[48]
Additional event data.
Definition ena.h:11
Error codes.
#define DBGC(...)
Definition compiler.h:505
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define ENOEXEC
Exec format error.
Definition errno.h:520
#define ENOMEM
Not enough space.
Definition errno.h:535
#define ENOTSUP
Operation not supported.
Definition errno.h:590
#define ENOTTY
Inappropriate I/O control operation.
Definition errno.h:595
#define FILE_SECBOOT(_status)
Declare a file's UEFI Secure Boot permission status.
Definition compiler.h:926
#define LOG_ERR
Error: error conditions.
Definition syslog.h:36
#define LOG_NOTICE
Notice: normal but significant conditions.
Definition syslog.h:42
static int require_trusted_images_permanent
Prevent changes to image trust requirement.
Definition image.c:75
struct image * alloc_image(struct uri *uri)
Allocate executable image.
Definition image.c:124
#define EACCES_UNTRUSTED
Definition image.c:49
struct image * find_image(const char *name)
Find image by name.
Definition image.c:376
struct list_head images
List of registered images.
Definition image.c:59
char * image_strip_suffix(struct image *image)
Strip dot suffix from image name, if present.
Definition image.c:206
void unregister_image(struct image *image)
Unregister executable image.
Definition image.c:358
void free_image(struct refcnt *refcnt)
Free executable image.
Definition image.c:86
int image_exec(struct image *image)
Execute image.
Definition image.c:414
#define EACCES_PERMANENT
Definition image.c:53
int image_set_data(struct image *image, const void *data, size_t len)
Set image data.
Definition image.c:270
static int require_trusted_images
Current image trust requirement.
Definition image.c:72
struct image * find_image_tag(struct image_tag *tag)
Find image by tag.
Definition image.c:393
int image_set_uri(struct image *image, struct uri *uri)
Set image URI.
Definition image.c:153
int register_image(struct image *image)
Register executable image.
Definition image.c:315
int image_set_cmdline(struct image *image, const char *cmdline)
Set image command line.
Definition image.c:226
int image_replace(struct image *replacement)
Set replacement image.
Definition image.c:529
int image_set_name(struct image *image, const char *name)
Set image name.
Definition image.c:181
struct image * image_memory(const char *name, const void *data, size_t len)
Create registered image from block of memory.
Definition image.c:609
static int image_probe(struct image *image)
Determine image type.
Definition image.c:289
const char * image_argument(struct image *image, const char *key)
Find argument within image command line.
Definition image.c:653
int image_set_len(struct image *image, size_t len)
Set image length.
Definition image.c:245
int image_select(struct image *image)
Select image for execution.
Definition image.c:565
int image_set_trust(int require_trusted, int permanent)
Change image trust requirement.
Definition image.c:584
Executable images.
static struct image * image_get(struct image *image)
Increment reference count on an image.
Definition image.h:240
struct image_tag current_image
#define IMAGE_STATIC_NAME
Image name is statically allocated.
Definition image.h:92
#define IMAGE_REGISTERED
Image is registered.
Definition image.h:77
#define IMAGE_TRUSTED
Image is trusted.
Definition image.h:80
static struct image * image_tag(struct image *image, struct image_tag *tag)
Tag image.
Definition image.h:297
static void image_put(struct image *image)
Decrement reference count on an image.
Definition image.h:250
#define IMAGE_TAGS
Image tag table.
Definition image.h:181
#define IMAGE_STATIC
Image is statically allocated.
Definition image.h:89
#define for_each_image(image)
Iterate over all registered images.
Definition image.h:191
#define __image_tag
An image tag.
Definition image.h:184
struct image_tag selected_image
#define IMAGE_AUTO_UNREGISTER
Image will be automatically unregistered after execution.
Definition image.h:83
#define IMAGE_TYPES
Executable image type table.
Definition image.h:167
User memory allocation.
void * urealloc(void *ptr, size_t new_size)
Reallocate external memory.
static __always_inline void ufree(void *ptr)
Free external memory.
Definition umalloc.h:68
String functions.
void * memcpy(void *dest, const void *src, size_t len) __nonnull
Access to external ("user") memory.
Linked lists.
#define LIST_HEAD_INIT(list)
Initialise a static list head.
Definition list.h:31
#define list_add_tail(new, head)
Add a new entry to the tail of a list.
Definition list.h:94
#define list_del(list)
Delete an entry from a list.
Definition list.h:120
void * zalloc(size_t size)
Allocate cleared memory.
Definition malloc.c:662
uint32_t cmdline
Definition multiboot.h:4
static void(* free)(struct refcnt *refcnt))
Definition refcnt.h:55
#define ref_init(refcnt, free)
Initialise a reference counter.
Definition refcnt.h:65
#define container_of(ptr, type, field)
Get containing structure.
Definition stddef.h:36
char * strerror(int errno)
Retrieve string representation of error number.
Definition strerror.c:79
int strcmp(const char *first, const char *second)
Compare strings.
Definition string.c:174
char * strdup(const char *src)
Duplicate string.
Definition string.c:394
char * strstr(const char *haystack, const char *needle)
Find substring.
Definition string.c:310
char * strrchr(const char *src, int character)
Find rightmost character within a string.
Definition string.c:290
size_t strlen(const char *src)
Get length of string.
Definition string.c:244
An image tag.
Definition image.h:173
An executable image type.
Definition image.h:95
int(* exec)(struct image *image)
Execute image.
Definition image.h:113
An executable image.
Definition image.h:24
unsigned int flags
Flags.
Definition image.h:40
struct image * replacement
Replacement image.
Definition image.h:73
struct refcnt refcnt
Reference count.
Definition image.h:26
struct image_type * type
Image type, if known.
Definition image.h:59
const void * data
Read-only data.
Definition image.h:51
char * name
Name.
Definition image.h:38
size_t len
Length of raw file image.
Definition image.h:56
struct list_head list
List of registered images.
Definition image.h:29
struct uri * uri
URI of image.
Definition image.h:32
char * cmdline
Command line to pass to image.
Definition image.h:43
void * rwdata
Writable data.
Definition image.h:53
A doubly-linked list entry (or list head)
Definition list.h:19
A reference counter.
Definition refcnt.h:27
int count
Current reference count.
Definition refcnt.h:33
A Uniform Resource Identifier.
Definition uri.h:65
const char * path
Path (after URI decoding)
Definition uri.h:81
const char * opaque
Opaque part.
Definition uri.h:71
System logger.
#define syslog(priority, fmt,...)
Write message to system log.
Definition syslog.h:94
#define for_each_table_entry(pointer, table)
Iterate through all entries within a linker table.
Definition tables.h:386
Uniform Resource Identifiers.
static struct uri * uri_get(struct uri *uri)
Increment URI reference count.
Definition uri.h:195
static void uri_put(struct uri *uri)
Decrement URI reference count.
Definition uri.h:206
int snprintf(char *buf, size_t size, const char *fmt,...)
Write a formatted string to a buffer.
Definition vsprintf.c:383