syseventadm.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* syseventadm - command to administer the sysevent.conf registry
* - administers the general purpose event framework
*
* The current implementation of the registry using files in
* /etc/sysevent/config, files are named as event specifications
* are added with the combination of the vendor, publisher, event
* class and subclass strings:
*
* [<vendor>,][<publisher>,][<class>,]sysevent.conf
*
*/
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <door.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <strings.h>
#include <unistd.h>
#include <synch.h>
#include <syslog.h>
#include <thread.h>
#include <limits.h>
#include <locale.h>
#include <assert.h>
#include <libsysevent.h>
#include <zone.h>
#include <sys/sysevent_impl.h>
#include <sys/modctl.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/systeminfo.h>
#include <sys/wait.h>
#include "syseventadm.h"
#include "syseventadm_msg.h"
#ifndef DEBUG
#undef assert
#define assert(EX) ((void)0)
#endif
static char *whoami = NULL;
static char *root_dir = "";
static char *arg_vendor = NULL;
static char *arg_publisher = NULL;
static char *arg_class = NULL;
static char *arg_subclass = NULL;
static char *arg_username = NULL;
static char *arg_path = NULL;
static int arg_nargs = 0;
static char **arg_args = NULL;
static int lock_fd;
static char lock_file[PATH_MAX + 1];
extern char *optarg;
extern int optind;
static int
usage_gen()
{
(void) fprintf(stderr, MSG_USAGE_INTRO);
(void) fprintf(stderr, MSG_USAGE_OPTIONS);
(void) fprintf(stderr, "\n"
"\tsyseventadm add ...\n"
"\tsyseventadm remove ...\n"
"\tsyseventadm list ...\n"
"\tsyseventadm restart\n"
"\tsyseventadm help\n");
return (EXIT_USAGE);
}
static int
serve_syseventdotconf(int argc, char **argv, char *cmd)
{
int c;
int rval;
while ((c = getopt(argc, argv, "R:v:p:c:s:u:")) != EOF) {
switch (c) {
case 'R':
/*
* Alternate root path for install, etc.
*/
set_root_dir(optarg);
break;
case 'v':
arg_vendor = optarg;
break;
case 'p':
arg_publisher = optarg;
break;
case 'c':
arg_class = optarg;
break;
case 's':
arg_subclass = optarg;
break;
case 'u':
arg_username = optarg;
break;
default:
return (usage());
}
}
if (optind < argc) {
arg_path = argv[optind++];
if (optind < argc) {
arg_nargs = argc - optind;
arg_args = argv + optind;
}
}
enter_lock(root_dir);
if (strcmp(cmd, "add") == 0) {
rval = add_cmd();
} else if (strcmp(cmd, "list") == 0) {
rval = list_remove_cmd(CMD_LIST);
} else if (strcmp(cmd, "remove") == 0) {
rval = list_remove_cmd(CMD_REMOVE);
} else if (strcmp(cmd, "restart") == 0) {
rval = restart_cmd();
} else {
rval = usage();
}
exit_lock();
return (rval);
}
int
main(int argc, char **argv)
{
char *cmd;
int rval;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
if ((whoami = strrchr(argv[0], '/')) == NULL) {
whoami = argv[0];
} else {
whoami++;
}
if (argc == 1) {
return (usage_gen());
}
cmd = argv[optind++];
/* Allow non-privileged users to get the help messages */
if (strcmp(cmd, "help") == 0) {
rval = usage_gen();
return (rval);
}
if (getuid() != 0) {
(void) fprintf(stderr, MSG_NOT_ROOT, whoami);
exit(EXIT_PERM);
}
if (strcmp(cmd, "evc") != 0 && getzoneid() != GLOBAL_ZONEID) {
(void) fprintf(stderr, MSG_NOT_GLOBAL, whoami);
exit(EXIT_PERM);
}
if (strcmp(cmd, "add") == 0 ||
strcmp(cmd, "remove") == 0 || strcmp(cmd, "list") == 0 ||
strcmp(cmd, "restart") == 0) {
rval = serve_syseventdotconf(argc, argv, cmd);
} else {
rval = usage_gen();
}
return (rval);
}
static void
enter_lock(char *root_dir)
{
struct flock lock;
if (snprintf(lock_file, sizeof (lock_file), "%s%s/%s", root_dir,
SYSEVENT_CONFIG_DIR, LOCK_FILENAME) >= sizeof (lock_file)) {
(void) fprintf(stderr, MSG_LOCK_PATH_ERR, whoami, lock_file);
exit(EXIT_CMD_FAILED);
}
lock_fd = open(lock_file, O_CREAT|O_RDWR, 0644);
if (lock_fd < 0) {
(void) fprintf(stderr, MSG_LOCK_CREATE_ERR,
whoami, lock_file, strerror(errno));
exit(EXIT_CMD_FAILED);
}
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
retry:
if (fcntl(lock_fd, F_SETLKW, &lock) == -1) {
if (errno == EAGAIN || errno == EINTR)
goto retry;
(void) close(lock_fd);
(void) fprintf(stderr, MSG_LOCK_SET_ERR,
whoami, lock_file, strerror(errno));
exit(EXIT_CMD_FAILED);
}
}
static void
exit_lock()
{
struct flock lock;
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
if (fcntl(lock_fd, F_SETLK, &lock) == -1) {
(void) fprintf(stderr, MSG_LOCK_CLR_ERR,
whoami, lock_file, strerror(errno));
}
if (close(lock_fd) == -1) {
(void) fprintf(stderr, MSG_LOCK_CLOSE_ERR,
whoami, lock_file, strerror(errno));
}
}
static void
set_root_dir(char *dir)
{
root_dir = sc_strdup(dir);
}
static char *usage_msg[] = {
"\n"
"\tsyseventadm add [-R <rootdir>] [-v vendor] [-p publisher]\n"
"\t[-c class] [-s subclass] [-u username] path [args]\n"
"\n"
"\tsyseventadm remove [-R <rootdir>] [-v vendor] [-p publisher]\n"
"\t[-c class] [-s subclass] [-u username] [path [args]]\n"
"\n"
"\tsyseventadm list [-R <rootdir>] [-v vendor] [-p publisher]\n"
"\t[-c class] [-s subclass] [-u username] [path [args]]\n"
};
static int
usage()
{
char **msgs;
int i;
msgs = usage_msg;
for (i = 0; i < sizeof (usage_msg)/sizeof (char *); i++) {
(void) fputs(*msgs++, stderr);
}
return (EXIT_USAGE);
}
static int
add_cmd(void)
{
char fname[MAXPATHLEN+1];
int need_comma = 0;
int noptions = 0;
struct stat st;
FILE *fp;
str_t *line;
int i;
/*
* At least one of vendor/publisher/class must be specified.
* Subclass is only defined within the context of class.
* For add, path must also be specified.
*/
if (arg_vendor)
noptions++;
if (arg_publisher)
noptions++;
if (arg_class)
noptions++;
if (noptions == 0 || (arg_subclass && arg_class == NULL)) {
return (usage());
}
if (arg_path == NULL)
return (usage());
/*
* Generate the sysevent.conf file name
*/
(void) strcpy(fname, root_dir);
(void) strcat(fname, SYSEVENT_CONFIG_DIR);
(void) strcat(fname, "/");
if (arg_vendor) {
(void) strcat(fname, arg_vendor);
need_comma = 1;
}
if (arg_publisher) {
if (need_comma)
(void) strcat(fname, ",");
(void) strcat(fname, arg_publisher);
need_comma = 1;
}
if (arg_class) {
if (need_comma)
(void) strcat(fname, ",");
(void) strcat(fname, arg_class);
}
(void) strcat(fname, SYSEVENT_CONF_SUFFIX);
/*
* Prepare the line to be written to the sysevent.conf file
*/
line = initstr(128);
strcats(line, arg_class == NULL ? "-" : arg_class);
strcatc(line, ' ');
strcats(line, arg_subclass == NULL ? "-" : arg_subclass);
strcatc(line, ' ');
strcats(line, arg_vendor == NULL ? "-" : arg_vendor);
strcatc(line, ' ');
strcats(line, arg_publisher == NULL ? "-" : arg_publisher);
strcatc(line, ' ');
strcats(line, arg_username == NULL ? "-" : arg_username);
strcatc(line, ' ');
strcats(line, "- - ");
strcats(line, arg_path);
if (arg_nargs) {
for (i = 0; i < arg_nargs; i++) {
strcatc(line, ' ');
strcats(line, arg_args[i]);
}
}
if (stat(fname, &st) == -1) {
if (creat(fname, 0644) == -1) {
(void) fprintf(stderr, MSG_CANNOT_CREATE,
whoami, fname, strerror(errno));
freestr(line);
return (EXIT_CMD_FAILED);
}
}
fp = fopen(fname, "a");
if (fp == NULL) {
(void) fprintf(stderr, MSG_CANNOT_OPEN,
whoami, fname, strerror(errno));
freestr(line);
return (EXIT_CMD_FAILED);
}
(void) fprintf(fp, "%s\n", line->s_str);
freestr(line);
if (fclose(fp) == -1) {
(void) fprintf(stderr, MSG_CLOSE_ERROR,
whoami, fname, strerror(errno));
return (EXIT_CMD_FAILED);
}
if (chmod(fname, 0444) == -1) {
(void) fprintf(stderr, MSG_CHMOD_ERROR,
whoami, fname, strerror(errno));
return (EXIT_CMD_FAILED);
}
return (EXIT_OK);
}
static int
list_remove_cmd(int cmd)
{
struct dirent *dp;
DIR *dir;
char path[MAXPATHLEN+1];
char fname[MAXPATHLEN+1];
char *suffix;
char **dirlist = NULL;
int list_size = 0;
int list_alloc = 0;
char **p;
int rval;
int result;
/*
* For the remove cmd, at least one of vendor/publisher/class/username
* path must be specified. Subclass is only defined within the
* context of a class.
*/
if (cmd == CMD_REMOVE) {
int noptions = 0;
if (arg_vendor)
noptions++;
if (arg_publisher)
noptions++;
if (arg_class)
noptions++;
if (arg_username)
noptions++;
if (arg_path)
noptions++;
if (noptions == 0 || (arg_subclass && arg_class == NULL)) {
return (usage());
}
}
(void) strcpy(path, root_dir);
(void) strcat(path, SYSEVENT_CONFIG_DIR);
if ((dir = opendir(path)) == NULL) {
(void) fprintf(stderr, MSG_CANNOT_OPEN_DIR,
whoami, path, strerror(errno));
return (EXIT_CMD_FAILED);
}
while ((dp = readdir(dir)) != NULL) {
if (dp->d_name[0] == '.')
continue;
if ((strlen(dp->d_name) == 0) ||
(strcmp(dp->d_name, "lost+found") == 0))
continue;
suffix = strrchr(dp->d_name, ',');
if (suffix && strcmp(suffix, SYSEVENT_CONF_SUFFIX) == 0) {
(void) strcpy(fname, path);
(void) strcat(fname, "/");
(void) strcat(fname, dp->d_name);
dirlist = build_strlist(dirlist,
&list_size, &list_alloc, fname);
}
}
if (closedir(dir) == -1) {
(void) fprintf(stderr, MSG_CLOSE_DIR_ERROR,
whoami, path, strerror(errno));
return (EXIT_CMD_FAILED);
}
rval = EXIT_NO_MATCH;
if (dirlist) {
for (p = dirlist; *p != NULL; p++) {
switch (cmd) {
case CMD_LIST:
result = list_file(*p);
break;
case CMD_REMOVE:
result = remove_file(*p);
break;
}
if (rval == EXIT_NO_MATCH &&
result != EXIT_NO_MATCH)
rval = result;
}
}
return (rval);
}
static int
list_file(char *fname)
{
FILE *fp;
str_t *line;
serecord_t *sep;
int rval = EXIT_NO_MATCH;
fp = fopen(fname, "r");
if (fp == NULL) {
(void) fprintf(stderr, MSG_CANNOT_OPEN,
whoami, fname, strerror(errno));
return (EXIT_CMD_FAILED);
}
for (;;) {
line = read_next_line(fp);
if (line == NULL)
break;
sep = parse_line(line);
if (sep != NULL) {
if (matches_serecord(sep)) {
print_serecord(stdout, sep);
rval = EXIT_OK;
}
free_serecord(sep);
}
freestr(line);
}
(void) fclose(fp);
return (rval);
}
static int
remove_file(char *fname)
{
FILE *fp;
FILE *tmp_fp;
str_t *line;
char *raw_line;
serecord_t *sep;
char tmp_name[MAXPATHLEN+1];
int is_empty = 1;
fp = fopen(fname, "r");
if (fp == NULL) {
(void) fprintf(stderr, MSG_CANNOT_OPEN,
whoami, fname, strerror(errno));
return (EXIT_CMD_FAILED);
}
if (check_for_removes(fp) == 0) {
(void) fclose(fp);
return (EXIT_NO_MATCH);
}
rewind(fp);
(void) strcpy(tmp_name, root_dir);
(void) strcat(tmp_name, SYSEVENT_CONFIG_DIR);
(void) strcat(tmp_name, "/tmp.XXXXXX");
if (mktemp(tmp_name) == NULL) {
(void) fprintf(stderr, "unable to make tmp file name\n");
return (EXIT_CMD_FAILED);
}
if (creat(tmp_name, 0644) == -1) {
(void) fprintf(stderr, MSG_CANNOT_CREATE,
whoami, tmp_name, strerror(errno));
return (EXIT_CMD_FAILED);
}
tmp_fp = fopen(tmp_name, "a");
if (tmp_fp == NULL) {
(void) fprintf(stderr, MSG_CANNOT_OPEN,
whoami, tmp_name, strerror(errno));
(void) unlink(tmp_name);
(void) fclose(fp);
return (EXIT_CMD_FAILED);
}
for (;;) {
line = read_next_line(fp);
if (line == NULL)
break;
raw_line = sc_strdup(line->s_str);
sep = parse_line(line);
if (sep == NULL) {
(void) fputs(line->s_str, tmp_fp);
} else {
if (!matches_serecord(sep)) {
is_empty = 0;
(void) fprintf(tmp_fp, "%s\n", raw_line);
}
free_serecord(sep);
}
freestr(line);
sc_strfree(raw_line);
}
(void) fclose(fp);
if (fclose(tmp_fp) == -1) {
(void) fprintf(stderr, MSG_CLOSE_ERROR,
whoami, tmp_name, strerror(errno));
}
if (is_empty) {
if (unlink(tmp_name) == -1) {
(void) fprintf(stderr, MSG_CANNOT_UNLINK,
whoami, tmp_name, strerror(errno));
return (EXIT_CMD_FAILED);
}
if (unlink(fname) == -1) {
(void) fprintf(stderr, MSG_CANNOT_UNLINK,
whoami, fname, strerror(errno));
return (EXIT_CMD_FAILED);
}
} else {
if (unlink(fname) == -1) {
(void) fprintf(stderr, MSG_CANNOT_UNLINK,
whoami, fname, strerror(errno));
return (EXIT_CMD_FAILED);
}
if (rename(tmp_name, fname) == -1) {
(void) fprintf(stderr, MSG_CANNOT_RENAME,
whoami, tmp_name, fname, strerror(errno));
return (EXIT_CMD_FAILED);
}
if (chmod(fname, 0444) == -1) {
(void) fprintf(stderr, MSG_CHMOD_ERROR,
whoami, fname, strerror(errno));
return (EXIT_CMD_FAILED);
}
}
return (EXIT_OK);
}
static int
check_for_removes(FILE *fp)
{
str_t *line;
serecord_t *sep;
for (;;) {
line = read_next_line(fp);
if (line == NULL)
break;
sep = parse_line(line);
if (sep != NULL) {
if (matches_serecord(sep)) {
free_serecord(sep);
freestr(line);
return (1);
}
free_serecord(sep);
}
freestr(line);
}
return (0);
}
static int
matches_serecord(serecord_t *sep)
{
char *line;
char *lp;
char *token;
int i;
if (arg_vendor &&
strcmp(arg_vendor, sep->se_vendor) != 0) {
return (0);
}
if (arg_publisher &&
strcmp(arg_publisher, sep->se_publisher) != 0) {
return (0);
}
if (arg_class &&
strcmp(arg_class, sep->se_class) != 0) {
return (0);
}
if (arg_subclass &&
strcmp(arg_subclass, sep->se_subclass) != 0) {
return (0);
}
if (arg_username &&
strcmp(arg_username, sep->se_user) != 0) {
return (0);
}
if (arg_path &&
strcmp(arg_path, sep->se_path) != 0) {
return (0);
}
if (arg_nargs > 0) {
line = sc_strdup(sep->se_args);
lp = line;
for (i = 0; i < arg_nargs; i++) {
token = next_field(&lp);
if (strcmp(arg_args[i], token) != 0) {
sc_strfree(line);
return (0);
}
}
sc_strfree(line);
}
return (1);
}
static void
print_serecord(FILE *fp, serecord_t *sep)
{
str_t *line;
line = initstr(128);
if (strcmp(sep->se_vendor, "-") != 0) {
strcats(line, "vendor=");
strcats(line, sep->se_vendor);
strcats(line, " ");
}
if (strcmp(sep->se_publisher, "-") != 0) {
strcats(line, "publisher=");
strcats(line, sep->se_publisher);
strcats(line, " ");
}
if (strcmp(sep->se_class, "-") != 0) {
strcats(line, "class=");
strcats(line, sep->se_class);
strcats(line, " ");
if (strcmp(sep->se_subclass, "-") != 0) {
strcats(line, "subclass=");
strcats(line, sep->se_subclass);
strcats(line, " ");
}
}
if (strcmp(sep->se_user, "-") != 0) {
strcats(line, "username=");
strcats(line, sep->se_user);
strcats(line, " ");
}
strcats(line, sep->se_path);
if (sep->se_args) {
strcats(line, " ");
strcats(line, sep->se_args);
}
strcats(line, "\n");
(void) fputs(line->s_str, fp);
freestr(line);
}
static int
restart_cmd(void)
{
if (system("pkill -HUP syseventd") == -1) {
(void) fprintf(stderr, MSG_RESTART_FAILED,
whoami, strerror(errno));
return (EXIT_CMD_FAILED);
}
return (EXIT_OK);
}
static str_t *
read_next_line(FILE *fp)
{
char *lp;
str_t *line;
line = initstr(128);
lp = fstrgets(line, fp);
if (lp == NULL) {
freestr(line);
return (NULL);
}
*(lp + strlen(lp)-1) = 0;
return (line);
}
static serecord_t *
parse_line(str_t *line)
{
char *lp;
char *vendor, *publisher;
char *class, *subclass;
char *user;
char *reserved1, *reserved2;
char *path, *args;
serecord_t *sep;
lp = line->s_str;
if (*lp == 0 || *lp == '#') {
return (NULL);
}
if ((class = next_field(&lp)) != NULL) {
subclass = next_field(&lp);
if (lp == NULL)
return (NULL);
vendor = next_field(&lp);
if (lp == NULL)
return (NULL);
publisher = next_field(&lp);
if (lp == NULL)
return (NULL);
user = next_field(&lp);
if (lp == NULL)
return (NULL);
reserved1 = next_field(&lp);
if (lp == NULL)
return (NULL);
reserved2 = next_field(&lp);
if (lp == NULL)
return (NULL);
path = next_field(&lp);
if (lp == NULL)
return (NULL);
args = skip_spaces(&lp);
}
sep = sc_malloc(sizeof (serecord_t));
sep->se_vendor = sc_strdup(vendor);
sep->se_publisher = sc_strdup(publisher);
sep->se_class = sc_strdup(class);
sep->se_subclass = sc_strdup(subclass);
sep->se_user = sc_strdup(user);
sep->se_reserved1 = sc_strdup(reserved1);
sep->se_reserved2 = sc_strdup(reserved2);
sep->se_path = sc_strdup(path);
sep->se_args = (args == NULL) ? NULL : sc_strdup(args);
return (sep);
}
static void
free_serecord(serecord_t *sep)
{
sc_strfree(sep->se_vendor);
sc_strfree(sep->se_publisher);
sc_strfree(sep->se_class);
sc_strfree(sep->se_subclass);
sc_strfree(sep->se_user);
sc_strfree(sep->se_reserved1);
sc_strfree(sep->se_reserved2);
sc_strfree(sep->se_path);
sc_strfree(sep->se_args);
sc_free(sep, sizeof (serecord_t));
}
/*
* skip_spaces() - skip to next non-space character
*/
static char *
skip_spaces(char **cpp)
{
char *cp = *cpp;
while (*cp == ' ' || *cp == '\t')
cp++;
if (*cp == 0) {
*cpp = 0;
return (NULL);
}
return (cp);
}
/*
* Get next white-space separated field.
* next_field() will not check any characters on next line.
* Each entry is composed of a single line.
*/
static char *
next_field(char **cpp)
{
char *cp = *cpp;
char *start;
while (*cp == ' ' || *cp == '\t')
cp++;
if (*cp == 0) {
*cpp = 0;
return (NULL);
}
start = cp;
while (*cp && *cp != ' ' && *cp != '\t')
cp++;
if (*cp != 0)
*cp++ = 0;
*cpp = cp;
return (start);
}
/*
* The following functions are simple wrappers/equivalents
* for malloc, realloc, free, strdup and a special free
* for strdup.
*/
static void *
sc_malloc(size_t n)
{
void *p;
p = malloc(n);
if (p == NULL) {
no_mem_err();
}
return (p);
}
/*ARGSUSED*/
static void *
sc_realloc(void *p, size_t current, size_t n)
{
p = realloc(p, n);
if (p == NULL) {
no_mem_err();
}
return (p);
}
/*ARGSUSED*/
static void
sc_free(void *p, size_t n)
{
free(p);
}
static char *
sc_strdup(char *cp)
{
char *new;
new = malloc((unsigned)(strlen(cp) + 1));
if (new == NULL) {
no_mem_err();
}
(void) strcpy(new, cp);
return (new);
}
static void
sc_strfree(char *s)
{
if (s)
free(s);
}
/*
* The following functions provide some simple dynamic string
* capability. This module has no hard-coded maximum string
* lengths and should be able to parse and generate arbitrarily
* long strings, macro expansion and command lines.
*
* Each string must be explicitly allocated and freed.
*/
/*
* Allocate a dynamic string, with a hint to indicate how
* much memory to dynamically add to the string as it grows
* beyond its existing bounds, so as to avoid excessive
* reallocs as a string grows.
*/
static str_t *
initstr(int hint)
{
str_t *str;
str = sc_malloc(sizeof (str_t));
str->s_str = NULL;
str->s_len = 0;
str->s_alloc = 0;
str->s_hint = hint;
return (str);
}
/*
* Free a dynamically-allocated string
*/
static void
freestr(str_t *str)
{
if (str->s_str) {
sc_free(str->s_str, str->s_alloc);
}
sc_free(str, sizeof (str_t));
}
/*
* Reset a dynamically-allocated string, allows reuse
* rather than freeing the old and allocating a new one.
*/
static void
resetstr(str_t *str)
{
str->s_len = 0;
}
/*
* Concatenate a (simple) string onto a dynamically-allocated string
*/
static void
strcats(str_t *str, char *s)
{
char *new_str;
int len = str->s_len + strlen(s) + 1;
if (str->s_alloc < len) {
new_str = (str->s_str == NULL) ? sc_malloc(len+str->s_hint) :
sc_realloc(str->s_str, str->s_alloc, len+str->s_hint);
str->s_str = new_str;
str->s_alloc = len + str->s_hint;
}
(void) strcpy(str->s_str + str->s_len, s);
str->s_len = len - 1;
}
/*
* Concatenate a character onto a dynamically-allocated string
*/
static void
strcatc(str_t *str, int c)
{
char *new_str;
int len = str->s_len + 2;
if (str->s_alloc < len) {
new_str = (str->s_str == NULL) ? sc_malloc(len+str->s_hint) :
sc_realloc(str->s_str, str->s_alloc, len+str->s_hint);
str->s_str = new_str;
str->s_alloc = len + str->s_hint;
}
*(str->s_str + str->s_len) = (char)c;
*(str->s_str + str->s_len + 1) = 0;
str->s_len++;
}
/*
* fgets() equivalent using a dynamically-allocated string
*/
static char *
fstrgets(str_t *line, FILE *fp)
{
int c;
resetstr(line);
while ((c = fgetc(fp)) != EOF) {
strcatc(line, c);
if (c == '\n')
break;
}
if (line->s_len == 0)
return (NULL);
return (line->s_str);
}
#define INITIAL_LISTSIZE 4
#define INCR_LISTSIZE 4
static char **
build_strlist(
char **argvlist,
int *size,
int *alloc,
char *str)
{
int n;
if (*size + 1 > *alloc) {
if (*alloc == 0) {
*alloc = INITIAL_LISTSIZE;
n = sizeof (char *) * (*alloc + 1);
argvlist = (char **)malloc(n);
if (argvlist == NULL)
no_mem_err();
} else {
*alloc += INCR_LISTSIZE;
n = sizeof (char *) * (*alloc + 1);
argvlist = (char **)realloc(argvlist, n);
if (argvlist == NULL)
no_mem_err();
}
}
argvlist[*size] = strdup(str);
*size += 1;
argvlist[*size] = NULL;
return (argvlist);
}
static void
no_mem_err()
{
(void) fprintf(stderr, MSG_NO_MEM, whoami);
exit_lock();
exit(EXIT_NO_MEM);
/*NOTREACHED*/
}