diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/cons/cons.c | 5 | ||||
-rw-r--r-- | sys/include/lib/string.h | 1 | ||||
-rw-r--r-- | sys/include/sys/limits.h | 2 | ||||
-rw-r--r-- | sys/include/sys/proc.h | 3 | ||||
-rw-r--r-- | sys/include/sys/signal.h | 2 | ||||
-rw-r--r-- | sys/include/sys/termios.h | 29 | ||||
-rw-r--r-- | sys/include/sys/vsr.h | 165 | ||||
-rw-r--r-- | sys/kern/kern_sig.c | 6 | ||||
-rw-r--r-- | sys/kern/kern_stub.c | 12 | ||||
-rw-r--r-- | sys/kern/kern_vsr.c | 344 | ||||
-rw-r--r-- | sys/lib/string/strdup.c | 52 |
11 files changed, 617 insertions, 4 deletions
diff --git a/sys/dev/cons/cons.c b/sys/dev/cons/cons.c index 88cbcbe..d9d727a 100644 --- a/sys/dev/cons/cons.c +++ b/sys/dev/cons/cons.c @@ -356,6 +356,7 @@ dev_write(dev_t dev, struct sio_txn *sio, int flags) static int dev_read(dev_t dev, struct sio_txn *sio, int flags) { + struct cons_screen *scr = &g_root_scr; struct cons_input input; uint8_t *p; int retval; @@ -369,7 +370,7 @@ dev_read(dev_t dev, struct sio_txn *sio, int flags) return -EFAULT; } - retval = cons_ibuf_pop(&g_root_scr, &input); + retval = cons_ibuf_pop(scr, &input); if (retval < 0) { return -EAGAIN; } @@ -386,7 +387,7 @@ dev_read(dev_t dev, struct sio_txn *sio, int flags) n -= 2; /* Try to get the next byte */ - retval = cons_ibuf_pop(&g_root_scr, &input); + retval = cons_ibuf_pop(scr, &input); if (retval < 0) { break; } diff --git a/sys/include/lib/string.h b/sys/include/lib/string.h index c09e6f4..3255ae5 100644 --- a/sys/include/lib/string.h +++ b/sys/include/lib/string.h @@ -35,6 +35,7 @@ size_t strlen(const char *s); char *itoa(int64_t value, char *buf, int base); +char *strdup(const char *s); int vsnprintf(char *s, size_t size, const char *fmt, va_list ap); int snprintf(char *s, size_t size, const char *fmt, ...); diff --git a/sys/include/sys/limits.h b/sys/include/sys/limits.h index f6aed9d..963d113 100644 --- a/sys/include/sys/limits.h +++ b/sys/include/sys/limits.h @@ -36,4 +36,6 @@ #define ARG_MAX 4096 #define CHAR_BIT 8 #define CPU_MAX 256 +#define VSR_MAX_DOMAIN 16 +#define VSR_MAX_CAPSULE 16 #endif /* !_SYS_LIMITS_H_ */ diff --git a/sys/include/sys/proc.h b/sys/include/sys/proc.h index 9cc9238..89fe638 100644 --- a/sys/include/sys/proc.h +++ b/sys/include/sys/proc.h @@ -39,6 +39,8 @@ #include <sys/syscall.h> #include <sys/exec.h> #include <sys/ucred.h> +#include <sys/limits.h> +#include <sys/vsr.h> #include <sys/filedesc.h> #include <sys/signal.h> #include <sys/vnode.h> @@ -88,6 +90,7 @@ struct proc { struct ucred cred; struct ksiginfo *ksig_list[PROC_SIGMAX]; struct filedesc *fds[PROC_MAX_FILEDES]; + struct vsr_domain *vsr_tab[VSR_MAX_DOMAIN]; struct mmap_lgdr *mlgdr; struct vcache *vcache; struct spinlock vcache_lock; diff --git a/sys/include/sys/signal.h b/sys/include/sys/signal.h index 9fc767d..eaf2d41 100644 --- a/sys/include/sys/signal.h +++ b/sys/include/sys/signal.h @@ -37,6 +37,7 @@ #define SIGFPE 8 /* Floating point exception */ #define SIGKILL 9 /* Kill */ #define SIGSEGV 11 /* Segmentation violation */ +#define SIGTERM 15 /* Terminate gracefully */ typedef uint32_t sigset_t; @@ -80,5 +81,6 @@ int sigismember(const sigset_t *set, int signo); void sigfpe_default(int signo); void sigkill_default(int signo); void sigsegv_default(int signo); +void sigterm_default(int signo); #endif /* _KERNEL */ #endif /* !_SYS_SIGNAL_H_ */ diff --git a/sys/include/sys/termios.h b/sys/include/sys/termios.h index 27339f1..a3ba794 100644 --- a/sys/include/sys/termios.h +++ b/sys/include/sys/termios.h @@ -33,8 +33,33 @@ /* * c_iflag: Input flags */ -#define ISTRIP 0x00000000 -#define ICRNL 0x00000001 +#define ISTRIP 0x00000001 /* Strip char */ +#define ICRNL 0x00000002 /* Map CR to NL */ +#define BRKINT 0x00000004 /* Signal interrupt on break */ +#define IGNBRK 0x00000008 /* Ignore break condition */ +#define IGNCR 0x00000010 /* Ignore CR */ +#define IGNPAR 0x00000020 /* Ignore chars with parity errors */ +#define INCLR 0x00000040 /* Map NL to CR */ +#define INPCK 0x00000080 /* Enable input parity check */ +#define IXANY 0x00000100 /* Enable any char to restart output */ +#define IXOFF 0x00000200 /* Enable start/stop control */ +#define PARMRK 0x00000400 /* Mark parity errors */ + +/* + * c_oflag: Output flags + */ +#define OPOST 0x00000001 /* Post-process output */ +#define ONLCR 0x00000002 /* Map NL to CR-NL on output */ +#define OCRNL 0x00000004 /* Map CR to NL on output */ +#define ONOCR 0x00000008 /* Map CR to output at col 0 */ +#define ONLRET 0x00000010 /* NL performs CR function */ +#define OFILL 0x00000020 /* Use fill chars for delay */ +#define NLDLY 0x00000040 /* Select newline type */ +#define CRDLY 0x00000080 /* Select carriage-return delays */ +#define TABDLY 0x00000100 /* Select horizontal-tab delays */ +#define BSDLY 0x00000200 /* Select backspace delays */ +#define VTDLY 0x00000400 /* Select veritcal tab delays */ +#define FFDLY 0x00000800 /* Select form-feed delays */ #define NCCS 20 diff --git a/sys/include/sys/vsr.h b/sys/include/sys/vsr.h new file mode 100644 index 0000000..e63cce1 --- /dev/null +++ b/sys/include/sys/vsr.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_VSR_H_ +#define _SYS_VSR_H_ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/param.h> +#include <sys/ucred.h> +#include <sys/limits.h> +#if defined(_KERNEL) +#include <sys/mutex.h> +#endif /* _KERNEL */ + +struct proc; + +#define VSR_FILE 0x00000000 /* Represented by file */ + +/* + * Defines the access semantics of whether + * r/w operations should be passed down to the + * global state or soley affecting a per-process + * shallow copy. + */ +typedef uint32_t vsr_mode_t; + +/* + * The Virtual System Resource namespace consists of + * domains containing named "capsules". The domain is + * simply a table indexed by a type value e.g. VSR_FILE + * and a capsule is simply a structure containing global data + * as well as a shallow copy which is controlled locally by the + * process. The capsule also contains various access semantics + * that help the VSR subsystem determine whether the access should + * be passed down globally or virtualized locally within the process. + */ +typedef uint8_t vsr_domain_t; + +/* + * VSR mode bits + */ +#define VSR_GLOB_WRITE BIT(0) /* Writes are global */ +#define VSR_GLOB_READ BIT(1) /* Reads are global */ +#define VSR_GLOB_CRED BIT(2) /* Global for specific creds */ + +#if defined(_KERNEL) + +struct vsr_capsule; + +/* + * VSR capsule operations + * + * @reclaim: Cleanup resources + */ +struct capsule_ops { + int(*reclaim)(struct vsr_capsule *cap, int flags); +}; + +/* + * Virtual system resource access + * semantics. + * + * @glob: Global data + * @shallow: Local per process copy + * @mode: VSR mode (see VSR_GLOB_*) + * @cred: Creds (used if VSR_GLOBAL_CRED set) + */ +struct vsr_access { + void *glob; + void *shallow; + vsr_mode_t mode; + struct ucred cred; +}; + +/* + * A virtual system resource capsule containing + * resource owner specific data and hashmap + * buckets. + * + * @name: Capsule name (e.g., "consfeat"), must be freed + * @data: Owner specific data + * @shadow: Local shadow copy (per-process) + * @buckets: Hashmap buckets + * @link: Bucket link + * @ops: Capsule operations + * @lock: Mutex lock protecting fields + */ +struct vsr_capsule { + char *name; + void *data; + void *shadow; + TAILQ_HEAD(, vsr_capsule) buckets; + TAILQ_ENTRY(vsr_capsule) link; + struct capsule_ops ops; + struct mutex lock; +}; + +/* + * Virtual system resource table containg + * VSRs for various types. + * + * Each VSR table belongs to a VSR domain + * (e.g., VSR_FILE). + * + * @ncaps: Number of capsules + * @is_init: Set if hashmap is set up + * @capsules: VSR capsule hashmap + */ +struct vsr_table { + struct vsr_capsule *capsules[VSR_MAX_CAPSULE]; +}; + +/* + * Virtual system resource domain (VSR). + * + * A VSR is represented by a specific VSR type + * (see VSR_*). Each VSR has a table of VSR capsules + * looked up by a VSR capsule name. + * + * One per process. + * + * @type: VSR type + * @table: VSR table + */ +struct vsr_domain { + int type; + struct vsr_table table; +}; + +void vsr_init_domains(struct proc *td); +void vsr_destroy_domains(struct proc *td); + +struct vsr_domain *vsr_new_domain(struct proc *td, vsr_domain_t type); +struct vsr_capsule *vsr_new_capsule(struct proc *td, vsr_domain_t type, const char *name); +struct vsr_capsule *vsr_lookup_capsule(struct proc *td, vsr_domain_t type, const char *name); + +#endif /* _KERNEL */ +#endif /* !_SYS_VSR_H_ */ diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index 58bd52d..044de7b 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -58,6 +58,12 @@ static struct sigaction sa_tab[] = { .sa_flags = 0, .sa_sigaction = NULL }, + [SIGTERM] = { + .sa_handler = sigterm_default, + .sa_mask = 0, + .sa_flags = 0, + .sa_sigaction = NULL + } }; /* diff --git a/sys/kern/kern_stub.c b/sys/kern/kern_stub.c index 17c6e54..a9a56ac 100644 --- a/sys/kern/kern_stub.c +++ b/sys/kern/kern_stub.c @@ -70,6 +70,18 @@ sigsegv_default(int signo) exit1(td, 0); } +void +sigterm_default(int signo) +{ + static struct proc *td; + + td = this_td(); + syslog_silence(false); + kprintf(OMIT_TIMESTAMP "Terminated (pid=%d)\n", td->pid); + syslog_silence(true); + exit1(td, 0); +} + int dev_noread(void) { diff --git a/sys/kern/kern_vsr.c b/sys/kern/kern_vsr.c new file mode 100644 index 0000000..c59be1e --- /dev/null +++ b/sys/kern/kern_vsr.c @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/vsr.h> +#include <sys/proc.h> +#include <sys/param.h> +#include <sys/limits.h> +#include <sys/syslog.h> +#include <vm/dynalloc.h> +#include <string.h> + +#define pr_trace(fmt, ...) kprintf("vsr: " fmt, ##__VA_ARGS__) +#define pr_error(...) pr_trace(__VA_ARGS__) + +static uint32_t +fnv1_hash(const char *s) +{ + uint32_t hash = 2166136261UL; + const uint8_t *p = (uint8_t *)s; + + while (*p != '\0') { + hash ^= *p; + hash = hash * 0x01000193; + ++p; + } + + return hash; +} + +/* + * Add a VSR capsule to a domain. + */ +static void +vsr_domain_add(struct vsr_domain *vsp, struct vsr_capsule *cap) +{ + struct vsr_table *tab; + struct vsr_capsule **slot; + uint32_t hash; + + if (vsp == NULL || cap == NULL) { + return; + } + + if (cap->name == NULL) { + pr_error("vsr_domain_add: cap->name == NULL\n"); + return; + } + + tab = &vsp->table; + hash = fnv1_hash(cap->name); + slot = &tab->capsules[hash % VSR_MAX_CAPSULE]; + + /* If this slot is free, set it */ + if (*slot == NULL) { + *slot = cap; + return; + } + + /* Handle collision */ + TAILQ_INSERT_TAIL(&(*slot)->buckets, cap, link); +} + +/* + * Handle VSR domain hashmap collisions. + * + * @slot: Slot that we have collided with + * @name: Name to lookup + * + * Returns the pointer to the actual capsule if the + * collision has been resolved, otherwise, NULL if the + * entry to look up was not found. + */ +static struct vsr_capsule * +vsr_domain_clash(struct vsr_capsule *slot, const char *name) +{ + struct vsr_capsule *cap_ent; + + TAILQ_FOREACH(cap_ent, &slot->buckets, link) { + if (cap_ent == NULL) { + continue; + } + + if (strcmp(cap_ent->name, name) == 0) { + return cap_ent; + } + } + + return NULL; +} + +/* + * Lookup a capsule within a VSR domain + * by name. + * + * @vsp: Domain to lookup within + * @name: Name to use as lookup key + * + * Returns NULL if no entry was found. + */ +static struct vsr_capsule * +vfs_domain_lookup(struct vsr_domain *vsp, const char *name) +{ + uint32_t hash; + struct vsr_table *tab; + struct vsr_capsule **slot; + + if (vsp == NULL || name == NULL) { + return NULL; + } + + tab = &vsp->table; + hash = fnv1_hash(name); + slot = &tab->capsules[hash % VSR_MAX_CAPSULE]; + + if (*slot == NULL) { + return NULL; + } + + if (strcmp((*slot)->name, name) != 0) { + return vsr_domain_clash(*slot, name); + } + + return *slot; +} + +/* + * Destroy a VSR capsule + * + * @capule: Capsule to destroy + */ +static void +vsr_destroy_capsule(struct vsr_capsule *capsule) +{ + struct vsr_capsule *bucket; + struct capsule_ops *ops; + + if (capsule->name != NULL) { + dynfree(capsule->name); + capsule->name = NULL; + } + + ops = &capsule->ops; + if (ops->reclaim != NULL) { + ops->reclaim(capsule, 0); + } + + TAILQ_FOREACH(bucket, &capsule->buckets, link) { + if (bucket == NULL) { + continue; + } + vsr_destroy_capsule(bucket); + } + + /* Release any held locks */ + mutex_release(&capsule->lock); +} + +/* + * Destroy a VSR table + * + * @tab: Table to destroy. + */ +static void +vsr_destroy_table(struct vsr_table *tab) +{ + struct vsr_capsule *capsule; + + if (tab == NULL) { + pr_error("vsr_destroy_table: tab is NULL\n"); + return; + } + + for (int i = 0; i < VSR_MAX_CAPSULE; ++i) { + if ((capsule = tab->capsules[i]) == NULL) { + continue; + } + + vsr_destroy_capsule(capsule); + } +} + +/* + * Allocate a new VSR capsule and add it to + * VSR domain. + * + * @type: Domain type (e.g., VSR_FILE) + * @name: Capsule name (e.g., "mod0.data") + * @sz: Length of capsulized data + */ +struct vsr_capsule * +vsr_new_capsule(struct proc *td, vsr_domain_t type, const char *name) +{ + struct vsr_capsule *capsule; + struct vsr_domain *domain; + + /* Valid args? */ + if (type >= VSR_MAX_DOMAIN || td == NULL) { + return NULL; + } + + /* + * The VSR domain must be registered for + * us to add any capsules to it. + */ + if ((domain = td->vsr_tab[type]) == NULL) { + pr_error("VSR domain %d not registered\n", type); + return NULL; + } + + /* Allocate a new capsule */ + capsule = dynalloc(sizeof(*capsule)); + if (capsule == NULL) { + return NULL; + } + + memset(capsule, 0, sizeof(*capsule)); + capsule->name = strdup(name); + + TAILQ_INIT(&capsule->buckets); + vsr_domain_add(domain, capsule); + return capsule; +} + +/* + * Allocate a new VSR domain and add it to + * a specific process. + * + * @type: VSR type (e.g., VSR_FILE) + */ +struct vsr_domain * +vsr_new_domain(struct proc *td, vsr_domain_t type) +{ + struct vsr_domain *domain; + + /* Valid args? */ + if (type >= VSR_MAX_DOMAIN || td == NULL) { + return NULL; + } + + /* + * Do not overwrite the entry if it is + * already allocated and log this anomalous + * activity. + */ + if (td->vsr_tab[type] != NULL) { + pr_error("[security]: type %d already allocated\n", type); + return NULL; + } + + domain = dynalloc(sizeof(*domain)); + if (domain == NULL) { + return NULL; + } + + /* Initialize the domain */ + memset(domain, 0, sizeof(*domain)); + domain->type = type; + + td->vsr_tab[type] = domain; + return domain; +} + +/* + * Lookup a capsule by name for the current + * process. + */ +struct vsr_capsule * +vsr_lookup_capsule(struct proc *td, vsr_domain_t type, const char *name) +{ + struct vsr_domain *domain; + + if (td == NULL) { + return NULL; + } + + /* + * The VSR domain must be registered for + * us to lookup any capsules from it. + */ + if ((domain = td->vsr_tab[type]) == NULL) { + pr_error("VSR domain %d not registered\n", type); + return NULL; + } + + return vfs_domain_lookup(domain, name); +} + +/* + * Initialize per-process domains + */ +void +vsr_init_domains(struct proc *td) +{ + if (vsr_new_domain(td, VSR_FILE) == NULL) { + pr_error("failed to initialize VSR file domain\n"); + } +} + +/* + * Destroy per-process domains + */ +void +vsr_destroy_domains(struct proc *td) +{ + struct vsr_domain *domain; + + if (td == NULL) { + return; + } + + for (int i = 0; i < VSR_MAX_DOMAIN; ++i) { + if ((domain = td->vsr_tab[i]) == NULL) { + continue; + } + + vsr_destroy_table(&domain->table); + } +} diff --git a/sys/lib/string/strdup.c b/sys/lib/string/strdup.c new file mode 100644 index 0000000..9c101bc --- /dev/null +++ b/sys/lib/string/strdup.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> +#include <vm/dynalloc.h> + +char * +strdup(const char *s) +{ + size_t s_len; + char *p; + + /* Make sure size is not zero */ + if ((s_len = strlen(s)) == 0) { + return NULL; + } + + /* Allocate new memory for this string */ + p = dynalloc(s_len + 1); + if (p == NULL) { + return NULL; + } + + memcpy(p, s, s_len); + return p; +} |