| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| #include <stdlib.h> |
| #include <ctype.h> |
| #include <unistd.h> |
| #include <limits.h> |
| #include <fcntl.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <utime.h> |
| #include <synch.h> |
| #include <strings.h> |
| #include <string.h> |
| #include <libintl.h> |
| #include <errno.h> |
| #include <auth_list.h> |
| #include <bsm/devices.h> |
| #include <bsm/devalloc.h> |
| |
| #define DA_DEFS "/etc/security/tsol/devalloc_defaults" |
| |
| extern int _readbufline(char *, int, char *, int, int *); |
| extern char *strtok_r(char *, const char *, char **); |
| extern char *_strtok_escape(char *, char *, char **); |
| extern int getdaon(void); |
| extern int da_matchname(devalloc_t *, char *); |
| extern int da_match(devalloc_t *, da_args *); |
| extern int dmap_matchname(devmap_t *, char *); |
| extern int dm_match(devmap_t *, da_args *); |
| |
| /* |
| * The following structure is for recording old entries to be retained. |
| * We read the entries from the database into a linked list in memory, |
| * then turn around and write them out again. |
| */ |
| typedef struct strentry { |
| struct strentry *se_next; |
| char se_str[4096 + 1]; |
| } strentry_t; |
| |
| /* |
| * da_check_longindevperm - |
| * reads /etc/logindevperm and checks if specified device is in the file. |
| * returns 1 if specified device found in /etc/logindevperm, else returns 0 |
| */ |
| int |
| da_check_logindevperm(char *devname) |
| { |
| int ret = 0; |
| int fd = -1; |
| int nlen, plen, slen, lineno, fsize; |
| char line[MAX_CANON]; |
| char *field_delims = " \t\n"; |
| char *fbuf = NULL; |
| char *ptr, *device; |
| char *lasts = NULL; |
| FILE *fp; |
| struct stat f_stat; |
| |
| /* |
| * check if /etc/logindevperm exists and get its size |
| */ |
| if ((fd = open(LOGINDEVPERM, O_RDONLY)) == -1) |
| return (0); |
| if (fstat(fd, &f_stat) != 0) { |
| (void) close(fd); |
| return (0); |
| } |
| fsize = f_stat.st_size; |
| if ((fbuf = (char *)malloc(fsize)) == NULL) { |
| (void) close(fd); |
| return (0); |
| } |
| if ((fp = fdopen(fd, "r")) == NULL) { |
| free(fbuf); |
| (void) close(fd); |
| return (0); |
| } |
| |
| /* |
| * read and parse /etc/logindevperm |
| */ |
| plen = nlen = lineno = 0; |
| while (fgets(line, MAX_CANON, fp) != NULL) { |
| lineno++; |
| if ((ptr = strchr(line, '#')) != NULL) |
| *ptr = '\0'; /* handle comments */ |
| if (strtok_r(line, field_delims, &lasts) == NULL) |
| continue; /* ignore blank lines */ |
| if (strtok_r(NULL, field_delims, &lasts) == NULL) |
| /* invalid entry */ |
| continue; |
| if ((ptr = strtok_r(NULL, field_delims, &lasts)) == NULL) |
| /* empty device list */ |
| continue; |
| nlen = strlen(ptr) + 1; /* +1 terminator */ |
| nlen += (plen + 1); |
| if (plen == 0) |
| slen = snprintf(fbuf, nlen, "%s", ptr); |
| else |
| slen = snprintf(fbuf + plen, nlen - plen, ":%s", ptr); |
| if (slen >= fsize) { |
| fbuf[0] = '\0'; |
| (void) fclose(fp); |
| return (slen); |
| } |
| plen += slen; |
| } |
| (void) fclose(fp); |
| |
| /* |
| * check if devname exists in /etc/logindevperm |
| */ |
| device = strtok_r(fbuf, ":", &lasts); |
| while (device != NULL) { |
| /* |
| * device and devname may be one of these types - |
| * /dev/xx |
| * /dev/xx* |
| * /dev/dir/xx |
| * /dev/dir/xx* |
| * /dev/dir/"*" |
| */ |
| if (strcmp(device, devname) == 0) { |
| /* /dev/xx, /dev/dir/xx */ |
| free(fbuf); |
| return (1); |
| } |
| if ((ptr = strrchr(device, KV_WILDCHAR)) != NULL) { |
| /* all wildcard types */ |
| *ptr = '\0'; |
| if (strncmp(device, devname, strlen(device)) == 0) { |
| free(fbuf); |
| return (1); |
| } |
| } |
| device = strtok_r(NULL, ":", &lasts); |
| } |
| |
| return (ret); |
| } |
| |
| /* |
| * _da_read_file - |
| * establishes readers/writer lock on fname; reads in the file if its |
| * contents changed since the last time we read it. |
| * returns size of buffer read, or -1 on failure. |
| */ |
| int |
| _da_read_file(char *fname, char **fbuf, time_t *ftime, rwlock_t *flock, |
| int flag) |
| { |
| int fd = -1; |
| int fsize = 0; |
| time_t newtime; |
| FILE *fp = NULL; |
| struct stat f_stat; |
| |
| if (flag & DA_FORCE) |
| *ftime = 0; |
| |
| /* check the size and the time stamp on the file */ |
| if (rw_rdlock(flock) != 0) |
| return (-1); |
| if (stat(fname, &f_stat) != 0) { |
| (void) rw_unlock(flock); |
| return (-1); |
| } |
| fsize = f_stat.st_size; |
| newtime = f_stat.st_mtime; |
| (void) rw_unlock(flock); |
| |
| while (newtime > *ftime) { |
| /* |
| * file has been modified since we last read it; or this |
| * is a forced read. |
| * read file into the buffer with rw lock. |
| */ |
| if (rw_wrlock(flock) != 0) |
| return (-1); |
| if ((fp = fopen(fname, "r")) == NULL) { |
| (void) rw_unlock(flock); |
| return (-1); |
| } |
| fd = fileno(fp); |
| if (*fbuf != NULL) { |
| free(*fbuf); |
| *fbuf = NULL; |
| } |
| if ((*fbuf = malloc(fsize)) == NULL) { |
| (void) rw_unlock(flock); |
| (void) close(fd); |
| return (-1); |
| } |
| if (read(fd, *fbuf, fsize) < fsize) { |
| free(*fbuf); |
| (void) rw_unlock(flock); |
| (void) close(fd); |
| return (-1); |
| } |
| (void) rw_unlock(flock); |
| /* |
| * verify that the file did not change just after we read it. |
| */ |
| if (rw_rdlock(flock) != 0) { |
| free(*fbuf); |
| (void) close(fd); |
| return (-1); |
| } |
| if (stat(fname, &f_stat) != 0) { |
| free(*fbuf); |
| (void) rw_unlock(flock); |
| (void) close(fd); |
| return (-1); |
| } |
| fsize = f_stat.st_size; |
| newtime = f_stat.st_mtime; |
| (void) rw_unlock(flock); |
| (void) close(fd); |
| *ftime = newtime; |
| } |
| |
| return (fsize); |
| } |
| |
| /* |
| * _update_zonename - |
| * add/remove current zone's name to the given devalloc_t. |
| */ |
| void |
| _update_zonename(da_args *dargs, devalloc_t *dap) |
| { |
| int i, j; |
| int oldsize, newsize; |
| int has_zonename = 0; |
| char *zonename; |
| kva_t *newkva, *oldkva; |
| kv_t *newdata, *olddata; |
| devinfo_t *devinfo; |
| |
| devinfo = dargs->devinfo; |
| oldkva = dap->da_devopts; |
| if (oldkva == NULL) { |
| if (dargs->optflag & DA_REMOVE_ZONE) |
| return; |
| if (dargs->optflag & DA_ADD_ZONE) { |
| newkva = _str2kva(devinfo->devopts, KV_ASSIGN, |
| KV_TOKEN_DELIMIT); |
| if (newkva != NULL) |
| dap->da_devopts = newkva; |
| return; |
| } |
| } |
| newsize = oldsize = oldkva->length; |
| if (kva_match(oldkva, DAOPT_ZONE)) |
| has_zonename = 1; |
| if (dargs->optflag & DA_ADD_ZONE) { |
| if ((zonename = index(devinfo->devopts, '=')) == NULL) |
| return; |
| zonename++; |
| if (has_zonename) { |
| (void) _insert2kva(oldkva, DAOPT_ZONE, zonename); |
| return; |
| } |
| newsize += 1; |
| } else if (dargs->optflag & DA_REMOVE_ZONE) { |
| if (has_zonename) { |
| newsize -= 1; |
| if (newsize == 0) { |
| /* |
| * If zone name was the only key/value pair, |
| * put 'reserved' in the empty slot. |
| */ |
| _kva_free(oldkva); |
| dap->da_devopts = NULL; |
| return; |
| } |
| } else { |
| return; |
| } |
| } |
| newkva = _new_kva(newsize); |
| newkva->length = 0; |
| newdata = newkva->data; |
| olddata = oldkva->data; |
| for (i = 0, j = 0; i < oldsize; i++) { |
| if ((dargs->optflag & DA_REMOVE_ZONE) && |
| (strcmp(olddata[i].key, DAOPT_ZONE) == 0)) |
| continue; |
| newdata[j].key = strdup(olddata[i].key); |
| newdata[j].value = strdup(olddata[i].value); |
| newkva->length++; |
| j++; |
| } |
| if (dargs->optflag & DA_ADD_ZONE) { |
| newdata[j].key = strdup(DAOPT_ZONE); |
| newdata[j].value = strdup(zonename); |
| newkva->length++; |
| } |
| _kva_free(oldkva); |
| dap->da_devopts = newkva; |
| } |
| |
| /* |
| * _dmap2str - |
| * converts a device_map entry into a printable string |
| * returns 0 on success, -1 on error. |
| */ |
| /*ARGSUSED*/ |
| static int |
| _dmap2str(da_args *dargs, devmap_t *dmp, char *buf, int size, const char *sep) |
| { |
| int length; |
| |
| length = snprintf(buf, size, "%s%s", dmp->dmap_devname, sep); |
| if (length >= size) |
| return (-1); |
| length += snprintf(buf + length, size - length, "%s%s", |
| dmp->dmap_devtype, sep); |
| if (length >= size) |
| return (-1); |
| length += snprintf(buf + length, size - length, "%s\n", |
| dmp->dmap_devlist); |
| if (length >= size) |
| return (-1); |
| return (0); |
| } |
| |
| /* |
| * _dmap2strentry - |
| * calls dmap2str to break given devmap_t into printable entry. |
| * returns pointer to decoded entry, NULL on error. |
| */ |
| static strentry_t * |
| _dmap2strentry(da_args *dargs, devmap_t *devmapp) |
| { |
| strentry_t *sep; |
| |
| if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL) |
| return (NULL); |
| if (_dmap2str(dargs, devmapp, sep->se_str, sizeof (sep->se_str), |
| KV_TOKEN_DELIMIT"\\\n\t") != 0) { |
| free(sep); |
| return (NULL); |
| } |
| return (sep); |
| } |
| |
| /* |
| * fix_optstr - |
| * removes trailing ':' from buf. |
| */ |
| void |
| fix_optstr(char *buf) |
| { |
| char *p = NULL; |
| |
| if (p = rindex(buf, ':')) |
| *p = ';'; |
| } |
| |
| /* |
| * _da2str - |
| * converts a device_allocate entry into a printable string |
| * returns 0 on success, -1 on error. |
| */ |
| static int |
| _da2str(da_args *dargs, devalloc_t *dap, char *buf, int size, const char *sep, |
| const char *osep) |
| { |
| int length; |
| int matching_entry = 0; |
| char **dnames; |
| |
| if (dargs->optflag & DA_UPDATE && |
| (dargs->optflag & DA_ADD_ZONE || |
| dargs->optflag & DA_REMOVE_ZONE) && |
| dargs->devnames) { |
| for (dnames = dargs->devnames; *dnames != NULL; dnames++) { |
| if (da_matchname(dap, *dnames)) { |
| matching_entry = 1; |
| break; |
| } |
| } |
| } |
| length = snprintf(buf, size, "%s%s", dap->da_devname, sep); |
| if (length >= size) |
| return (-1); |
| length += snprintf(buf + length, size - length, "%s%s", |
| dap->da_devtype, sep); |
| if (length >= size) |
| return (-1); |
| if (matching_entry) |
| _update_zonename(dargs, dap); |
| if ((dap->da_devopts == NULL) || ((dap->da_devopts->length == 1) && |
| (strcmp(dap->da_devopts->data->key, DA_RESERVED) == 0))) { |
| length += snprintf(buf + length, size - length, "%s%s", |
| DA_RESERVED, sep); |
| } else { |
| if (_kva2str(dap->da_devopts, buf + length, size - length, |
| KV_ASSIGN, (char *)osep) != 0) |
| return (-1); |
| length = strlen(buf); |
| } |
| if (dap->da_devopts) |
| fix_optstr(buf); |
| if (length >= size) |
| return (-1); |
| length += snprintf(buf + length, size - length, "%s%s", |
| DA_RESERVED, sep); |
| if (length >= size) |
| return (-1); |
| length += snprintf(buf + length, size - length, "%s%s", |
| dap->da_devauth ? dap->da_devauth : DA_ANYUSER, sep); |
| if (length >= size) |
| return (-1); |
| length += snprintf(buf + length, size - length, "%s\n", |
| dap->da_devexec ? dap->da_devexec : ""); |
| if (length >= size) |
| return (-1); |
| |
| return (0); |
| } |
| |
| /* |
| * _da2strentry - |
| * calls da2str to break given devalloc_t into printable entry. |
| * returns pointer to decoded entry, NULL on error. |
| */ |
| static strentry_t * |
| _da2strentry(da_args *dargs, devalloc_t *dap) |
| { |
| strentry_t *sep; |
| |
| if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL) |
| return (NULL); |
| if (_da2str(dargs, dap, sep->se_str, sizeof (sep->se_str), |
| KV_DELIMITER "\\\n\t", KV_TOKEN_DELIMIT "\\\n\t") != 0) { |
| free(sep); |
| return (NULL); |
| } |
| return (sep); |
| } |
| |
| /* |
| * _def2str |
| * converts da_defs_t into a printable string. |
| * returns 0 on success, -1 on error. |
| */ |
| static int |
| _def2str(da_defs_t *da_defs, char *buf, int size, const char *sep) |
| { |
| int length; |
| |
| length = snprintf(buf, size, "%s%s", da_defs->devtype, sep); |
| if (length >= size) |
| return (-1); |
| if (da_defs->devopts) { |
| if (_kva2str(da_defs->devopts, buf + length, size - length, |
| KV_ASSIGN, KV_DELIMITER) != 0) |
| return (-1); |
| length = strlen(buf); |
| } |
| if (length >= size) |
| return (-1); |
| |
| return (0); |
| } |
| |
| /* |
| * _def2strentry |
| * calls _def2str to break given da_defs_t into printable entry. |
| * returns pointer decoded entry, NULL on error. |
| */ |
| static strentry_t * |
| _def2strentry(da_defs_t *da_defs) |
| { |
| strentry_t *sep; |
| |
| if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL) |
| return (NULL); |
| if (_def2str(da_defs, sep->se_str, sizeof (sep->se_str), |
| KV_TOKEN_DELIMIT) != 0) { |
| free(sep); |
| return (NULL); |
| } |
| |
| return (sep); |
| } |
| |
| /* |
| * _build_defattrs |
| * cycles through all defattr entries, stores them in memory. removes |
| * entries with the given search_key (device type). |
| * returns 0 if given entry not found, 1 if given entry removed, 2 on |
| * error. |
| */ |
| static int |
| _build_defattrs(da_args *dargs, strentry_t **head_defent) |
| { |
| int rc = 0; |
| da_defs_t *da_defs; |
| strentry_t *tail_str, *tmp_str; |
| |
| setdadefent(); |
| while ((da_defs = getdadefent()) != NULL) { |
| rc = !(strcmp(da_defs->devtype, dargs->devinfo->devtype)); |
| if (rc && dargs->optflag & DA_ADD && |
| !(dargs->optflag & DA_FORCE)) { |
| /* |
| * During DA_ADD, we keep an existing entry unless |
| * we have DA_FORCE set to override that entry. |
| */ |
| dargs->optflag |= DA_NO_OVERRIDE; |
| rc = 0; |
| } |
| if (rc == 0) { |
| tmp_str = _def2strentry(da_defs); |
| if (tmp_str == NULL) { |
| freedadefent(da_defs); |
| enddadefent(); |
| return (2); |
| } |
| /* retaining defattr entry: tmp_str->se_str */ |
| tmp_str->se_next = NULL; |
| if (*head_defent == NULL) { |
| *head_defent = tail_str = tmp_str; |
| } else { |
| tail_str->se_next = tmp_str; |
| tail_str = tmp_str; |
| } |
| } |
| freedadefent(da_defs); |
| } |
| enddadefent(); |
| |
| return (rc); |
| } |
| |
| /* |
| * _build_lists - |
| * cycles through all the entries, stores them in memory. removes entries |
| * with the given search_key (device name or type). |
| * returns 0 if given entry not found, 1 if given entry removed, 2 on |
| * error. |
| */ |
| static int |
| _build_lists(da_args *dargs, strentry_t **head_devallocp, |
| strentry_t **head_devmapp) |
| { |
| int rc = 0; |
| devalloc_t *devallocp; |
| devmap_t *devmapp; |
| strentry_t *tail_str; |
| strentry_t *tmp_str; |
| |
| if (dargs->optflag & DA_MAPS_ONLY) |
| goto dmap_only; |
| |
| /* build device_allocate */ |
| setdaent(); |
| while ((devallocp = getdaent()) != NULL) { |
| rc = da_match(devallocp, dargs); |
| if (rc && dargs->optflag & DA_ADD && |
| !(dargs->optflag & DA_FORCE)) { |
| /* |
| * During DA_ADD, we keep an existing entry unless |
| * we have DA_FORCE set to override that entry. |
| */ |
| dargs->optflag |= DA_NO_OVERRIDE; |
| rc = 0; |
| } |
| if (rc == 0) { |
| tmp_str = _da2strentry(dargs, devallocp); |
| if (tmp_str == NULL) { |
| freedaent(devallocp); |
| enddaent(); |
| return (2); |
| } |
| /* retaining devalloc entry: tmp_str->se_str */ |
| tmp_str->se_next = NULL; |
| if (*head_devallocp == NULL) { |
| *head_devallocp = tail_str = tmp_str; |
| } else { |
| tail_str->se_next = tmp_str; |
| tail_str = tmp_str; |
| } |
| } |
| freedaent(devallocp); |
| } |
| enddaent(); |
| |
| dmap_only: |
| if (dargs->optflag & DA_ALLOC_ONLY) |
| return (rc); |
| |
| /* build device_maps */ |
| rc = 0; |
| setdmapent(); |
| while ((devmapp = getdmapent()) != NULL) { |
| rc = dm_match(devmapp, dargs); |
| if (rc && dargs->optflag & DA_ADD && |
| !(dargs->optflag & DA_FORCE)) { |
| /* |
| * During DA_ADD, we keep an existing entry unless |
| * we have DA_FORCE set to override that entry. |
| */ |
| dargs->optflag |= DA_NO_OVERRIDE; |
| rc = 0; |
| } |
| if (rc == 0) { |
| tmp_str = _dmap2strentry(dargs, devmapp); |
| if (tmp_str == NULL) { |
| freedmapent(devmapp); |
| enddmapent(); |
| return (2); |
| } |
| /* retaining devmap entry: tmp_str->se_str */ |
| tmp_str->se_next = NULL; |
| if (*head_devmapp == NULL) { |
| *head_devmapp = tail_str = tmp_str; |
| } else { |
| tail_str->se_next = tmp_str; |
| tail_str = tmp_str; |
| } |
| } |
| freedmapent(devmapp); |
| } |
| enddmapent(); |
| |
| return (rc); |
| } |
| |
| /* |
| * _write_defattrs |
| * writes current entries to devalloc_defaults. |
| */ |
| static void |
| _write_defattrs(FILE *fp, strentry_t *head_defent) |
| { |
| strentry_t *tmp_str; |
| |
| for (tmp_str = head_defent; tmp_str != NULL; |
| tmp_str = tmp_str->se_next) { |
| (void) fputs(tmp_str->se_str, fp); |
| (void) fputs("\n", fp); |
| } |
| |
| } |
| |
| /* |
| * _write_device_allocate - |
| * writes current entries in the list to device_allocate. |
| */ |
| static void |
| _write_device_allocate(char *odevalloc, FILE *dafp, strentry_t *head_devallocp) |
| { |
| int is_on = -1; |
| strentry_t *tmp_str; |
| struct stat dastat; |
| |
| (void) fseek(dafp, (off_t)0, SEEK_SET); |
| |
| /* |
| * if the devalloc on/off string existed before, |
| * put it back before anything else. |
| * we need to check for the string only if the file |
| * exists. |
| */ |
| if (stat(odevalloc, &dastat) == 0) { |
| is_on = da_is_on(); |
| if (is_on == 0) |
| (void) fputs(DA_OFF_STR, dafp); |
| else if (is_on == 1) |
| (void) fputs(DA_ON_STR, dafp); |
| } |
| tmp_str = head_devallocp; |
| while (tmp_str) { |
| (void) fputs(tmp_str->se_str, dafp); |
| (void) fputs("\n", dafp); |
| tmp_str = tmp_str->se_next; |
| } |
| } |
| |
| /* |
| * _write_device_maps - |
| * writes current entries in the list to device_maps. |
| */ |
| static void |
| _write_device_maps(FILE *dmfp, strentry_t *head_devmapp) |
| { |
| strentry_t *tmp_str; |
| |
| (void) fseek(dmfp, (off_t)0, SEEK_SET); |
| |
| tmp_str = head_devmapp; |
| while (tmp_str) { |
| (void) fputs(tmp_str->se_str, dmfp); |
| (void) fputs("\n", dmfp); |
| tmp_str = tmp_str->se_next; |
| } |
| } |
| |
| /* |
| * _write_new_defattrs |
| * writes the new entry to devalloc_defaults. |
| * returns 0 on success, -1 on error. |
| */ |
| static int |
| _write_new_defattrs(FILE *fp, da_args *dargs) |
| { |
| int count; |
| char *tok = NULL, *tokp = NULL; |
| char *lasts; |
| devinfo_t *devinfo = dargs->devinfo; |
| |
| if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1) |
| return (-1); |
| if (!devinfo->devopts) |
| return (0); |
| (void) fprintf(fp, "%s%s", (devinfo->devtype ? devinfo->devtype : ""), |
| KV_TOKEN_DELIMIT); |
| if ((tokp = (char *)malloc(strlen(devinfo->devopts))) != NULL) { |
| (void) strcpy(tokp, devinfo->devopts); |
| if ((tok = strtok_r(tokp, KV_DELIMITER, &lasts)) != NULL) { |
| (void) fprintf(fp, "%s", tok); |
| count = 1; |
| } |
| while ((tok = strtok_r(NULL, KV_DELIMITER, &lasts)) != NULL) { |
| if (count) |
| (void) fprintf(fp, "%s", KV_DELIMITER); |
| (void) fprintf(fp, "%s", tok); |
| count++; |
| } |
| } else { |
| (void) fprintf(fp, "%s", devinfo->devopts); |
| } |
| |
| return (0); |
| } |
| |
| /* |
| * _write_new_entry - |
| * writes the new devalloc_t to device_allocate or the new devmap_t to |
| * device_maps. |
| * returns 0 on success, -1 on error. |
| */ |
| static int |
| _write_new_entry(FILE *fp, da_args *dargs, int flag) |
| { |
| int count; |
| char *tok = NULL, *tokp = NULL; |
| char *lasts; |
| devinfo_t *devinfo = dargs->devinfo; |
| |
| if (flag & DA_MAPS_ONLY) |
| goto dmap_only; |
| |
| if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1) |
| return (-1); |
| |
| (void) fprintf(fp, "%s%s\\\n\t", |
| (devinfo->devname ? devinfo->devname : ""), KV_DELIMITER); |
| (void) fprintf(fp, "%s%s\\\n\t", |
| (devinfo->devtype ? devinfo->devtype : ""), KV_DELIMITER); |
| if (devinfo->devopts == NULL) { |
| (void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED, |
| KV_DELIMITER); |
| } else { |
| if ((tokp = (char *)malloc(strlen(devinfo->devopts))) != NULL) { |
| (void) strcpy(tokp, devinfo->devopts); |
| if ((tok = strtok_r(tokp, KV_TOKEN_DELIMIT, &lasts)) != |
| NULL) { |
| (void) fprintf(fp, "%s", tok); |
| count = 1; |
| } |
| while ((tok = strtok_r(NULL, KV_TOKEN_DELIMIT, |
| &lasts)) != NULL) { |
| if (count) |
| (void) fprintf(fp, "%s", |
| KV_TOKEN_DELIMIT "\\\n\t"); |
| (void) fprintf(fp, "%s", tok); |
| count++; |
| } |
| if (count) |
| (void) fprintf(fp, "%s", |
| KV_DELIMITER "\\\n\t"); |
| } else { |
| (void) fprintf(fp, "%s%s", devinfo->devopts, |
| KV_DELIMITER "\\\n\t"); |
| } |
| } |
| (void) fprintf(fp, "%s%s\\\n\t", DA_RESERVED, KV_DELIMITER); |
| (void) fprintf(fp, "%s%s\\\n\t", |
| (devinfo->devauths ? devinfo->devauths : DA_ANYUSER), |
| KV_DELIMITER); |
| (void) fprintf(fp, "%s\n", |
| (devinfo->devexec ? devinfo->devexec : KV_DELIMITER)); |
| |
| dmap_only: |
| if (flag & DA_ALLOC_ONLY) |
| return (0); |
| |
| if (fseek(fp, (off_t)0, SEEK_END) == (off_t)-1) |
| return (-1); |
| |
| (void) fprintf(fp, "%s%s\\\n", |
| (devinfo->devname ? devinfo->devname : ""), KV_TOKEN_DELIMIT); |
| (void) fprintf(fp, "\t%s%s\\\n", |
| (devinfo->devtype ? devinfo->devtype : ""), KV_TOKEN_DELIMIT); |
| (void) fprintf(fp, "\t%s\n", |
| (devinfo->devlist ? devinfo->devlist : KV_TOKEN_DELIMIT)); |
| |
| return (0); |
| } |
| |
| /* |
| * _da_lock_devdb - |
| * locks the database files; lock can be either broken explicitly by |
| * closing the fd of the lock file, or it expires automatically at process |
| * termination. |
| * returns fd of the lock file or -1 on error. |
| */ |
| int |
| _da_lock_devdb(char *rootdir) |
| { |
| int lockfd = -1; |
| char *lockfile; |
| char path[MAXPATHLEN]; |
| int size = sizeof (path); |
| |
| if (rootdir == NULL) { |
| lockfile = DA_DB_LOCK; |
| } else { |
| path[0] = '\0'; |
| if (snprintf(path, size, "%s%s", rootdir, DA_DB_LOCK) >= size) |
| return (-1); |
| lockfile = path; |
| } |
| |
| if ((lockfd = open(lockfile, O_RDWR | O_CREAT, 0600)) == -1) |
| /* cannot open lock file */ |
| return (-1); |
| |
| (void) fchown(lockfd, DA_UID, DA_GID); |
| |
| if (lseek(lockfd, (off_t)0, SEEK_SET) == -1) { |
| /* cannot position lock file */ |
| (void) close(lockfd); |
| return (-1); |
| } |
| if (lockf(lockfd, F_TLOCK, 0) == -1) { |
| /* cannot set lock */ |
| (void) close(lockfd); |
| return (-1); |
| } |
| (void) utime(lockfile, NULL); |
| |
| return (lockfd); |
| } |
| |
| /* |
| * da_open_devdb - |
| * opens one or both database files - device_allocate, device_maps - in |
| * the specified mode. |
| * locks the database files; lock is either broken explicitly by the |
| * caller by closing the lock file fd, or it expires automatically at |
| * process termination. |
| * writes the file pointer of opened file in the input args - dafp, dmfp. |
| * returns fd of the lock file on success, -2 if database file does not |
| * exist, -1 on other errors. |
| */ |
| int |
| da_open_devdb(char *rootdir, FILE **dafp, FILE **dmfp, int flag) |
| { |
| int oflag = 0; |
| int fda = -1; |
| int fdm = -1; |
| int lockfd = -1; |
| char *fname; |
| char *fmode; |
| char path[MAXPATHLEN]; |
| FILE *devfile; |
| |
| if ((dafp == NULL) && (dmfp == NULL)) |
| return (-1); |
| |
| if (flag & DA_RDWR) { |
| oflag = DA_RDWR; |
| fmode = "r+"; |
| } else if (flag & DA_RDONLY) { |
| oflag = DA_RDONLY; |
| fmode = "r"; |
| } |
| |
| if ((lockfd = _da_lock_devdb(rootdir)) == -1) |
| return (-1); |
| |
| if ((dafp == NULL) || (flag & DA_MAPS_ONLY)) |
| goto dmap_only; |
| |
| path[0] = '\0'; |
| |
| /* |
| * open the device allocation file |
| */ |
| if (rootdir == NULL) { |
| fname = DEVALLOC; |
| } else { |
| if (snprintf(path, sizeof (path), "%s%s", rootdir, |
| DEVALLOC) >= sizeof (path)) { |
| if (lockfd != -1) |
| (void) close(lockfd); |
| return (-1); |
| } |
| fname = path; |
| } |
| if ((fda = open(fname, oflag, DA_DBMODE)) == -1) { |
| if (lockfd != -1) |
| (void) close(lockfd); |
| return ((errno == ENOENT) ? -2 : -1); |
| } |
| if ((devfile = fdopen(fda, fmode)) == NULL) { |
| (void) close(fda); |
| if (lockfd != -1) |
| (void) close(lockfd); |
| return (-1); |
| } |
| *dafp = devfile; |
| (void) fchmod(fda, DA_DBMODE); |
| |
| if ((flag & DA_ALLOC_ONLY)) |
| goto out; |
| |
| dmap_only: |
| path[0] = '\0'; |
| /* |
| * open the device map file |
| */ |
| if (rootdir == NULL) { |
| fname = DEVMAP; |
| } else { |
| if (snprintf(path, sizeof (path), "%s%s", rootdir, |
| DEVMAP) >= sizeof (path)) { |
| (void) close(fda); |
| if (lockfd != -1) |
| (void) close(lockfd); |
| return (-1); |
| } |
| fname = path; |
| } |
| |
| if ((fdm = open(fname, oflag, DA_DBMODE)) == -1) { |
| if (lockfd != -1) |
| (void) close(lockfd); |
| return ((errno == ENOENT) ? -2 : -1); |
| } |
| |
| if ((devfile = fdopen(fdm, fmode)) == NULL) { |
| (void) close(fdm); |
| (void) close(fda); |
| if (lockfd != -1) |
| (void) close(lockfd); |
| return (-1); |
| } |
| *dmfp = devfile; |
| (void) fchmod(fdm, DA_DBMODE); |
| |
| out: |
| return (lockfd); |
| } |
| |
| /* |
| * _record_on_off - |
| * adds either DA_ON_STR or DA_OFF_STR to device_allocate |
| * returns 0 on success, -1 on error. |
| */ |
| static int |
| _record_on_off(da_args *dargs, FILE *tafp, FILE *dafp) |
| { |
| int dafd; |
| int nsize; |
| int nitems = 1; |
| int actionlen; |
| int str_found = 0; |
| int len = 0, nlen = 0, plen = 0; |
| char *ptr = NULL; |
| char *actionstr; |
| char *nbuf = NULL; |
| char line[MAX_CANON]; |
| struct stat dastat; |
| |
| if (dargs->optflag & DA_ON) |
| actionstr = DA_ON_STR; |
| else |
| actionstr = DA_OFF_STR; |
| actionlen = strlen(actionstr); |
| dafd = fileno(dafp); |
| if (fstat(dafd, &dastat) == -1) |
| return (-1); |
| |
| /* check the old device_allocate for on/off string */ |
| ptr = fgets(line, MAX_CANON, dafp); |
| if (ptr != NULL) { |
| if ((strcmp(line, DA_ON_STR) == 0) || |
| (strcmp(line, DA_OFF_STR) == 0)) { |
| str_found = 1; |
| nsize = dastat.st_size; |
| } |
| } |
| if (!ptr || !str_found) { |
| /* |
| * the file never had either the on or the off string; |
| * make room for it. |
| */ |
| str_found = 0; |
| nsize = dastat.st_size + actionlen + 1; |
| } |
| if ((nbuf = (char *)malloc(nsize)) == NULL) |
| return (-1); |
| nbuf[0] = '\0'; |
| /* put the on/off string */ |
| (void) strcpy(nbuf, actionstr); |
| nlen = strlen(nbuf); |
| plen = nlen; |
| if (ptr && !str_found) { |
| /* now put the first line that we read in fgets */ |
| nlen = plen + strlen(line) + 1; |
| len = snprintf(nbuf + plen, nlen - plen, "%s", line); |
| if (len >= nsize) { |
| free(nbuf); |
| return (-1); |
| } |
| plen += len; |
| } |
| |
| /* now get the rest of the old file */ |
| while (fgets(line, MAX_CANON, dafp) != NULL) { |
| nlen = plen + strlen(line) + 1; |
| len = snprintf(nbuf + plen, nlen - plen, "%s", line); |
| if (len >= nsize) { |
| free(nbuf); |
| return (-1); |
| } |
| plen += len; |
| } |
| len = strlen(nbuf) + 1; |
| if (len < nsize) |
| nbuf[len] = '\n'; |
| |
| /* write the on/off str + the old device_allocate to the temp file */ |
| if (fwrite(nbuf, nsize, nitems, tafp) < nitems) { |
| free(nbuf); |
| return (-1); |
| } |
| |
| free(nbuf); |
| |
| return (0); |
| } |
| |
| /* |
| * da_update_defattrs - |
| * writes default attributes to devalloc_defaults |
| * returns 0 on success, -1 on error. |
| */ |
| int |
| da_update_defattrs(da_args *dargs) |
| { |
| int rc = 0, lockfd = 0, tmpfd = 0; |
| char *defpath = DEFATTRS; |
| char *tmpdefpath = TMPATTRS; |
| FILE *tmpfp = NULL; |
| struct stat dstat; |
| strentry_t *head_defent = NULL; |
| |
| if (dargs == NULL) |
| return (0); |
| if ((lockfd = _da_lock_devdb(NULL)) == -1) |
| return (-1); |
| if ((tmpfd = open(tmpdefpath, O_RDWR|O_CREAT, DA_DBMODE)) == -1) { |
| (void) close(lockfd); |
| return (-1); |
| } |
| (void) fchown(tmpfd, DA_UID, DA_GID); |
| if ((tmpfp = fdopen(tmpfd, "r+")) == NULL) { |
| (void) close(tmpfd); |
| (void) unlink(tmpdefpath); |
| (void) close(lockfd); |
| return (-1); |
| } |
| /* |
| * examine all entries, remove an old one if required, check |
| * if a new one needs to be added. |
| */ |
| if (stat(defpath, &dstat) == 0) { |
| if ((rc = _build_defattrs(dargs, &head_defent)) != 0) { |
| if (rc == 1) { |
| (void) close(tmpfd); |
| (void) unlink(tmpdefpath); |
| (void) close(lockfd); |
| return (rc); |
| } |
| } |
| } |
| /* |
| * write back any existing entries. |
| */ |
| _write_defattrs(tmpfp, head_defent); |
| |
| if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) { |
| /* add new entries */ |
| rc = _write_new_defattrs(tmpfp, dargs); |
| (void) fclose(tmpfp); |
| } else { |
| (void) fclose(tmpfp); |
| } |
| if (rename(tmpdefpath, defpath) != 0) { |
| rc = -1; |
| (void) unlink(tmpdefpath); |
| } |
| (void) close(lockfd); |
| |
| return (rc); |
| } |
| |
| /* |
| * da_update_device - |
| * writes devices entries to device_allocate and device_maps. |
| * returns 0 on success, -1 on error. |
| */ |
| int |
| da_update_device(da_args *dargs) |
| { |
| int rc; |
| int tafd = -1, tmfd = -1; |
| int lockfd = -1; |
| char *rootdir = NULL; |
| char *apathp = NULL, *mpathp = NULL, *dapathp = NULL, |
| *dmpathp = NULL; |
| char apath[MAXPATHLEN], mpath[MAXPATHLEN], |
| dapath[MAXPATHLEN], dmpath[MAXPATHLEN]; |
| FILE *tafp = NULL, *tmfp = NULL, *dafp = NULL; |
| struct stat dastat; |
| devinfo_t *devinfo; |
| strentry_t *head_devmapp = NULL; |
| strentry_t *head_devallocp = NULL; |
| |
| if (dargs == NULL) |
| return (0); |
| |
| rootdir = dargs->rootdir; |
| devinfo = dargs->devinfo; |
| |
| /* |
| * adding/removing entries should be done in both |
| * device_allocate and device_maps. updates can be |
| * done in both or either of the files. |
| */ |
| if (dargs->optflag & DA_ADD || dargs->optflag & DA_REMOVE) { |
| if (dargs->optflag & DA_ALLOC_ONLY || |
| dargs->optflag & DA_MAPS_ONLY) |
| return (0); |
| } |
| |
| /* |
| * name, type and list are required fields for adding a new |
| * device. |
| */ |
| if ((dargs->optflag & DA_ADD) && |
| ((devinfo->devname == NULL) || |
| (devinfo->devtype == NULL) || |
| (devinfo->devlist == NULL))) { |
| return (-1); |
| } |
| |
| if (rootdir != NULL) { |
| if (snprintf(apath, sizeof (apath), "%s%s", rootdir, |
| TMPALLOC) >= sizeof (apath)) |
| return (-1); |
| apathp = apath; |
| if (snprintf(dapath, sizeof (dapath), "%s%s", rootdir, |
| DEVALLOC) >= sizeof (dapath)) |
| return (-1); |
| dapathp = dapath; |
| if (!(dargs->optflag & DA_ALLOC_ONLY)) { |
| if (snprintf(mpath, sizeof (mpath), "%s%s", rootdir, |
| TMPMAP) >= sizeof (mpath)) |
| return (-1); |
| mpathp = mpath; |
| if (snprintf(dmpath, sizeof (dmpath), "%s%s", rootdir, |
| DEVMAP) >= sizeof (dmpath)) |
| return (-1); |
| dmpathp = dmpath; |
| } |
| } else { |
| apathp = TMPALLOC; |
| dapathp = DEVALLOC; |
| mpathp = TMPMAP; |
| dmpathp = DEVMAP; |
| } |
| |
| if (dargs->optflag & DA_MAPS_ONLY) |
| goto dmap_only; |
| |
| /* |
| * Check if we are here just to record on/off status of |
| * device_allocation. |
| */ |
| if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF) |
| lockfd = da_open_devdb(dargs->rootdir, &dafp, NULL, |
| DA_RDONLY|DA_ALLOC_ONLY); |
| else |
| lockfd = _da_lock_devdb(rootdir); |
| if (lockfd == -1) |
| return (-1); |
| |
| if ((tafd = open(apathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) { |
| (void) close(lockfd); |
| (void) fclose(dafp); |
| return (-1); |
| } |
| (void) fchown(tafd, DA_UID, DA_GID); |
| if ((tafp = fdopen(tafd, "r+")) == NULL) { |
| (void) close(tafd); |
| (void) unlink(apathp); |
| (void) fclose(dafp); |
| (void) close(lockfd); |
| return (-1); |
| } |
| |
| /* |
| * We don't need to parse the file if we are here just to record |
| * on/off status of device_allocation. |
| */ |
| if (dargs->optflag & DA_ON || dargs->optflag & DA_OFF) { |
| if (_record_on_off(dargs, tafp, dafp) == -1) { |
| (void) close(tafd); |
| (void) unlink(apathp); |
| (void) fclose(dafp); |
| (void) close(lockfd); |
| return (-1); |
| } |
| (void) fclose(dafp); |
| goto out; |
| } |
| |
| /* |
| * examine all the entries, remove an old one if forced to, |
| * and check that they are suitable for updating. |
| * we need to do this only if the file exists already. |
| */ |
| if (stat(dapathp, &dastat) == 0) { |
| if ((rc = _build_lists(dargs, &head_devallocp, |
| &head_devmapp)) != 0) { |
| if (rc != 1) { |
| (void) close(tafd); |
| (void) unlink(apathp); |
| (void) close(lockfd); |
| return (rc); |
| } |
| } |
| } |
| |
| /* |
| * write back any existing devalloc entries, along with |
| * the devalloc on/off string. |
| */ |
| _write_device_allocate(dapathp, tafp, head_devallocp); |
| |
| if (dargs->optflag & DA_ALLOC_ONLY) |
| goto out; |
| |
| dmap_only: |
| if ((tmfd = open(mpathp, O_RDWR|O_CREAT, DA_DBMODE)) == -1) { |
| (void) close(tafd); |
| (void) unlink(apathp); |
| (void) close(lockfd); |
| return (-1); |
| } |
| (void) fchown(tmfd, DA_UID, DA_GID); |
| if ((tmfp = fdopen(tmfd, "r+")) == NULL) { |
| (void) close(tafd); |
| (void) unlink(apathp); |
| (void) close(tmfd); |
| (void) unlink(mpathp); |
| (void) close(lockfd); |
| return (-1); |
| } |
| |
| /* write back any existing devmap entries */ |
| if (head_devmapp != NULL) |
| _write_device_maps(tmfp, head_devmapp); |
| |
| out: |
| if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) { |
| /* add any new entries */ |
| rc = _write_new_entry(tafp, dargs, DA_ALLOC_ONLY); |
| (void) fclose(tafp); |
| |
| if (rc == 0) |
| rc = _write_new_entry(tmfp, dargs, DA_MAPS_ONLY); |
| (void) fclose(tmfp); |
| } else { |
| if (tafp) |
| (void) fclose(tafp); |
| if (tmfp) |
| (void) fclose(tmfp); |
| } |
| |
| rc = 0; |
| if (!(dargs->optflag & DA_MAPS_ONLY)) { |
| if (rename(apathp, dapathp) != 0) { |
| rc = -1; |
| (void) unlink(apathp); |
| } |
| } |
| if (!(dargs->optflag & DA_ALLOC_ONLY)) { |
| if (rename(mpathp, dmpathp) != 0) { |
| rc = -1; |
| (void) unlink(mpathp); |
| } |
| } |
| |
| (void) close(lockfd); |
| |
| return (rc); |
| } |
| |
| /* |
| * da_add_list - |
| * adds new /dev link name to the linked list of devices. |
| * returns 0 if link added successfully, -1 on error. |
| */ |
| int |
| da_add_list(devlist_t *dlist, char *link, int new_instance, int flag) |
| { |
| int instance; |
| int nlen, plen; |
| int new_entry = 0; |
| char *dtype, *dexec, *tname, *kval; |
| char *minstr = NULL, *maxstr = NULL; |
| char dname[DA_MAXNAME]; |
| kva_t *kva; |
| deventry_t *dentry = NULL, *nentry = NULL, *pentry = NULL; |
| da_defs_t *da_defs; |
| |
| if (dlist == NULL || link == NULL) |
| return (-1); |
| |
| dname[0] = '\0'; |
| if (flag & DA_AUDIO) { |
| dentry = dlist->audio; |
| tname = DA_AUDIO_NAME; |
| dtype = DA_AUDIO_TYPE; |
| dexec = DA_DEFAULT_AUDIO_CLEAN; |
| } else if (flag & DA_CD) { |
| dentry = dlist->cd; |
| tname = DA_CD_NAME; |
| dtype = DA_CD_TYPE; |
| dexec = DA_DEFAULT_DISK_CLEAN; |
| } else if (flag & DA_FLOPPY) { |
| dentry = dlist->floppy; |
| tname = DA_FLOPPY_NAME; |
| dtype = DA_FLOPPY_TYPE; |
| dexec = DA_DEFAULT_DISK_CLEAN; |
| } else if (flag & DA_TAPE) { |
| dentry = dlist->tape; |
| tname = DA_TAPE_NAME; |
| dtype = DA_TAPE_TYPE; |
| dexec = DA_DEFAULT_TAPE_CLEAN; |
| } else if (flag & DA_RMDISK) { |
| dentry = dlist->rmdisk; |
| tname = DA_RMDISK_NAME; |
| dtype = DA_RMDISK_TYPE; |
| dexec = DA_DEFAULT_DISK_CLEAN; |
| } else { |
| return (-1); |
| } |
| |
| for (nentry = dentry; nentry != NULL; nentry = nentry->next) { |
| pentry = nentry; |
| (void) sscanf(nentry->devinfo.devname, "%*[a-z]%d", &instance); |
| if (nentry->devinfo.instance == new_instance) |
| /* |
| * Add the new link name to the list of links |
| * that the device 'dname' has. |
| */ |
| break; |
| } |
| |
| if (nentry == NULL) { |
| /* |
| * Either this is the first entry ever, or no matching entry |
| * was found. Create a new one and add to the list. |
| */ |
| if (dentry == NULL) /* first entry ever */ |
| instance = 0; |
| else /* no matching entry */ |
| instance++; |
| (void) snprintf(dname, sizeof (dname), "%s%d", tname, instance); |
| if ((nentry = (deventry_t *)malloc(sizeof (deventry_t))) == |
| NULL) |
| return (-1); |
| if (pentry != NULL) |
| pentry->next = nentry; |
| new_entry = 1; |
| nentry->devinfo.devname = strdup(dname); |
| nentry->devinfo.devtype = dtype; |
| nentry->devinfo.devauths = DEFAULT_DEV_ALLOC_AUTH; |
| nentry->devinfo.devexec = dexec; |
| nentry->devinfo.instance = new_instance; |
| /* |
| * Look for default label range, authorizations and cleaning |
| * program in devalloc_defaults. If label range is not |
| * specified in devalloc_defaults, assume it to be admin_low |
| * to admin_high. |
| */ |
| minstr = DA_DEFAULT_MIN; |
| maxstr = DA_DEFAULT_MAX; |
| setdadefent(); |
| if (da_defs = getdadeftype(nentry->devinfo.devtype)) { |
| kva = da_defs->devopts; |
| if ((kval = kva_match(kva, DAOPT_MINLABEL)) != NULL) |
| minstr = strdup(kval); |
| if ((kval = kva_match(kva, DAOPT_MAXLABEL)) != NULL) |
| maxstr = strdup(kval); |
| if ((kval = kva_match(kva, DAOPT_AUTHS)) != NULL) |
| nentry->devinfo.devauths = strdup(kval); |
| if ((kval = kva_match(kva, DAOPT_CSCRIPT)) != NULL) |
| nentry->devinfo.devexec = strdup(kval); |
| freedadefent(da_defs); |
| } |
| enddadefent(); |
| kval = NULL; |
| nlen = strlen(DAOPT_MINLABEL) + strlen(KV_ASSIGN) + |
| strlen(minstr) + strlen(KV_TOKEN_DELIMIT) + |
| strlen(DAOPT_MAXLABEL) + strlen(KV_ASSIGN) + strlen(maxstr) |
| + 1; /* +1 for terminator */ |
| if (kval = (char *)malloc(nlen)) |
| (void) snprintf(kval, nlen, "%s%s%s%s%s%s%s", |
| DAOPT_MINLABEL, KV_ASSIGN, minstr, KV_TOKEN_DELIMIT, |
| DAOPT_MAXLABEL, KV_ASSIGN, maxstr); |
| nentry->devinfo.devopts = kval; |
| |
| nentry->devinfo.devlist = NULL; |
| nentry->next = NULL; |
| } |
| |
| nlen = strlen(link) + 1; /* +1 terminator */ |
| if (nentry->devinfo.devlist) { |
| plen = strlen(nentry->devinfo.devlist); |
| nlen = nlen + plen + 1; /* +1 for blank to separate entries */ |
| } else { |
| plen = 0; |
| } |
| |
| if ((nentry->devinfo.devlist = |
| (char *)realloc(nentry->devinfo.devlist, nlen)) == NULL) { |
| if (new_entry) { |
| nentry->devinfo.devname = NULL; |
| free(nentry->devinfo.devname); |
| nentry = NULL; |
| free(nentry); |
| if (pentry != NULL) |
| pentry->next = NULL; |
| } |
| return (-1); |
| } |
| |
| if (plen == 0) |
| (void) snprintf(nentry->devinfo.devlist, nlen, "%s", link); |
| else |
| (void) snprintf(nentry->devinfo.devlist + plen, nlen - plen, |
| " %s", link); |
| |
| if (pentry == NULL) { |
| /* |
| * This is the first entry of this device type. |
| */ |
| if (flag & DA_AUDIO) |
| dlist->audio = nentry; |
| else if (flag & DA_CD) |
| dlist->cd = nentry; |
| else if (flag & DA_FLOPPY) |
| dlist->floppy = nentry; |
| else if (flag & DA_TAPE) |
| dlist->tape = nentry; |
| else if (flag & DA_RMDISK) |
| dlist->rmdisk = nentry; |
| } |
| |
| return (0); |
| } |
| |
| /* |
| * da_remove_list - |
| * removes a /dev link name from the linked list of devices. |
| * returns type of device if link for that device removed |
| * successfully, else returns -1 on error. |
| * if all links for a device are removed, stores that device |
| * name in devname. |
| */ |
| int |
| da_remove_list(devlist_t *dlist, char *link, int type, char *devname, int size) |
| { |
| int flag; |
| int remove_dev = 0; |
| int nlen, plen, slen; |
| char *lasts, *lname, *oldlist; |
| struct stat rmstat; |
| deventry_t *dentry, *current, *prev; |
| |
| if (type != NULL) |
| flag = type; |
| else if (link == NULL) |
| return (-1); |
| else if (strstr(link, DA_AUDIO_NAME) || strstr(link, DA_SOUND_NAME)) |
| flag = DA_AUDIO; |
| else if (strstr(link, "dsk") || strstr(link, "rdsk") || |
| strstr(link, "sr") || strstr(link, "rsr")) |
| flag = DA_CD; |
| else if (strstr(link, "fd") || strstr(link, "rfd") || |
| strstr(link, "diskette") || strstr(link, "rdiskette")) |
| flag = DA_FLOPPY; |
| else if (strstr(link, DA_TAPE_NAME)) |
| flag = DA_TAPE; |
| else |
| flag = DA_RMDISK; |
| |
| switch (type) { |
| case DA_AUDIO: |
| dentry = dlist->audio; |
| break; |
| case DA_CD: |
| dentry = dlist->cd; |
| break; |
| case DA_FLOPPY: |
| dentry = dlist->floppy; |
| break; |
| case DA_TAPE: |
| dentry = dlist->tape; |
| break; |
| case DA_RMDISK: |
| dentry = dlist->rmdisk; |
| break; |
| default: |
| return (-1); |
| } |
| |
| if ((type != NULL) && (link == NULL)) { |
| for (current = dentry, prev = dentry; current != NULL; |
| current = current->next) { |
| oldlist = strdup(current->devinfo.devlist); |
| for (lname = strtok_r(oldlist, " ", &lasts); |
| lname != NULL; |
| lname = strtok_r(NULL, " ", &lasts)) { |
| if (stat(lname, &rmstat) != 0) { |
| remove_dev = 1; |
| goto remove_dev; |
| } |
| } |
| prev = current; |
| } |
| return (-1); |
| } |
| |
| for (current = dentry, prev = dentry; current != NULL; |
| current = current->next) { |
| plen = strlen(current->devinfo.devlist); |
| nlen = strlen(link); |
| if (plen == nlen) { |
| if (strcmp(current->devinfo.devlist, link) == 0) { |
| /* last name in the list */ |
| remove_dev = 1; |
| break; |
| } |
| } |
| if (strstr(current->devinfo.devlist, link)) { |
| nlen = plen - nlen + 1; |
| oldlist = strdup(current->devinfo.devlist); |
| if ((current->devinfo.devlist = |
| (char *)realloc(current->devinfo.devlist, |
| nlen)) == NULL) { |
| free(oldlist); |
| return (-1); |
| } |
| current->devinfo.devlist[0] = '\0'; |
| nlen = plen = slen = 0; |
| for (lname = strtok_r(oldlist, " ", &lasts); |
| lname != NULL; |
| lname = strtok_r(NULL, " ", &lasts)) { |
| if (strcmp(lname, link) == 0) |
| continue; |
| nlen = strlen(lname) + plen + 1; |
| if (plen == 0) { |
| slen = |
| snprintf(current->devinfo.devlist, |
| nlen, "%s", lname); |
| } else { |
| slen = |
| snprintf(current->devinfo.devlist + |
| plen, nlen - plen, " %s", |
| lname); |
| } |
| plen = plen + slen + 1; |
| } |
| free(oldlist); |
| break; |
| } |
| prev = current; |
| } |
| |
| remove_dev: |
| if (remove_dev == 1) { |
| (void) strlcpy(devname, current->devinfo.devname, size); |
| free(current->devinfo.devname); |
| free(current->devinfo.devlist); |
| current->devinfo.devname = current->devinfo.devlist = NULL; |
| prev->next = current->next; |
| free(current); |
| current = NULL; |
| } |
| if ((remove_dev == 1) && (prev->devinfo.devname == NULL)) { |
| if (prev->next) { |
| /* |
| * what we removed above was the first entry |
| * in the list. make the next entry to be the |
| * first. |
| */ |
| current = prev->next; |
| } else { |
| /* |
| * the matching entry was the only entry in the list |
| * for this type. |
| */ |
| current = NULL; |
| } |
| if (flag & DA_AUDIO) |
| dlist->audio = current; |
| else if (flag & DA_CD) |
| dlist->cd = current; |
| else if (flag & DA_FLOPPY) |
| dlist->floppy = current; |
| else if (flag & DA_TAPE) |
| dlist->tape = current; |
| else if (flag & DA_RMDISK) |
| dlist->rmdisk = current; |
| } |
| |
| return (flag); |
| } |
| |
| /* |
| * da_is_on - |
| * checks if device allocation feature is turned on. |
| * returns 1 if on, 0 if off, -1 if status string not |
| * found in device_allocate. |
| */ |
| int |
| da_is_on() |
| { |
| return (getdaon()); |
| } |
| |
| /* |
| * da_print_device - |
| * debug routine to print device entries. |
| */ |
| void |
| da_print_device(int flag, devlist_t *devlist) |
| { |
| deventry_t *entry, *dentry; |
| devinfo_t *devinfo; |
| |
| if (flag & DA_AUDIO) |
| dentry = devlist->audio; |
| else if (flag & DA_CD) |
| dentry = devlist->cd; |
| else if (flag & DA_FLOPPY) |
| dentry = devlist->floppy; |
| else if (flag & DA_TAPE) |
| dentry = devlist->tape; |
| else if (flag & DA_RMDISK) |
| dentry = devlist->rmdisk; |
| else |
| return; |
| |
| for (entry = dentry; entry != NULL; entry = entry->next) { |
| devinfo = &(entry->devinfo); |
| (void) fprintf(stdout, "name: %s\n", devinfo->devname); |
| (void) fprintf(stdout, "type: %s\n", devinfo->devtype); |
| (void) fprintf(stdout, "auth: %s\n", devinfo->devauths); |
| (void) fprintf(stdout, "exec: %s\n", devinfo->devexec); |
| (void) fprintf(stdout, "list: %s\n\n", devinfo->devlist); |
| } |
| } |