#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 <syslog.h>
#include <bsm/devices.h>
#include <bsm/devalloc.h>
#include <tsol/label.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 *);
extern int dmap_matchtype(devmap_t *dmap, char *type);
extern int dmap_matchdev(devmap_t *dmap, char *dev);
extern int dmap_exact_dev(devmap_t *dmap, char *dev, int *num);
extern char *dmap_physname(devmap_t *dmap);
typedef struct strentry {
struct strentry *se_next;
char se_str[4096 + 1];
} strentry_t;
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;
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, "rF")) == NULL) {
free(fbuf);
(void) close(fd);
return (0);
}
plen = nlen = lineno = 0;
while (fgets(line, MAX_CANON, fp) != NULL) {
lineno++;
if ((ptr = strchr(line, '#')) != NULL)
*ptr = '\0';
if (strtok_r(line, field_delims, &lasts) == NULL)
continue;
if (strtok_r(NULL, field_delims, &lasts) == NULL)
continue;
if ((ptr = strtok_r(NULL, field_delims, &lasts)) == NULL)
continue;
nlen = strlen(ptr) + 1;
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);
device = strtok_r(fbuf, ":", &lasts);
while (device != NULL) {
if (strcmp(device, devname) == 0) {
free(fbuf);
return (1);
}
if ((ptr = strrchr(device, KV_WILDCHAR)) != NULL) {
*ptr = '\0';
if (strncmp(device, devname, strlen(device)) == 0) {
free(fbuf);
return (1);
}
}
device = strtok_r(NULL, ":", &lasts);
}
return (ret);
}
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;
struct stat f_stat;
if (flag & DA_FORCE)
*ftime = 0;
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) {
if (rw_wrlock(flock) != 0)
return (-1);
if ((fd = open(fname, O_RDONLY)) == -1) {
(void) rw_unlock(flock);
return (-1);
}
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);
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);
}
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) {
_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;
}
static int
_dmap2str(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);
}
static strentry_t *
_dmap2strentry(devmap_t *devmapp)
{
strentry_t *sep;
if ((sep = (strentry_t *)malloc(sizeof (strentry_t))) == NULL)
return (NULL);
if (_dmap2str(devmapp, sep->se_str, sizeof (sep->se_str),
KV_TOKEN_DELIMIT"\\\n\t") != 0) {
free(sep);
return (NULL);
}
return (sep);
}
void
fix_optstr(char *buf)
{
char *p = NULL;
if (p = rindex(buf, ':'))
*p = ';';
}
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);
}
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);
}
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);
}
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);
}
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)) {
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);
}
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);
}
int
da_std_type(da_args *dargs, char *namebuf)
{
char *type = dargs->devinfo->devtype;
int system_labeled;
system_labeled = is_system_labeled();
if (strcmp(DA_AUDIO_TYPE, type) == 0) {
(void) strlcpy(namebuf, DA_AUDIO_NAME, DA_MAXNAME);
return (1);
}
if (strcmp(DA_CD_TYPE, type) == 0) {
if (system_labeled)
(void) strlcpy(namebuf, DA_CD_NAME, DA_MAXNAME);
else
(void) strlcpy(namebuf, DA_CD_TYPE, DA_MAXNAME);
return (1);
}
if (strcmp(DA_FLOPPY_TYPE, type) == 0) {
if (system_labeled)
(void) strlcpy(namebuf, DA_FLOPPY_NAME, DA_MAXNAME);
else
(void) strlcpy(namebuf, DA_FLOPPY_TYPE, DA_MAXNAME);
return (1);
}
if (strcmp(DA_TAPE_TYPE, type) == 0) {
if (system_labeled)
(void) strlcpy(namebuf, DA_TAPE_NAME, DA_MAXNAME);
else
(void) strlcpy(namebuf, DA_TAPE_TYPE, DA_MAXNAME);
return (1);
}
if (strcmp(DA_RMDISK_TYPE, type) == 0) {
(void) strlcpy(namebuf, DA_RMDISK_NAME, DA_MAXNAME);
return (1);
}
namebuf[0] = '\0';
return (0);
}
static int
allocatable(da_args *dargs)
{
if (!dargs->devinfo->devauths)
return (-1);
if (strcmp("*", dargs->devinfo->devauths) == 0)
return (0);
return (1);
}
static int
_rebuild_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;
uint64_t tmp_bitmap = 0;
uint_t tmp = 0;
char *realname;
int suffix;
int found = 0;
int stdtype = 1;
int is_allocatable = 1;
char new_devname[DA_MAXNAME + 1];
char defname[DA_MAXNAME + 1];
char errmsg[DA_MAXNAME + 1 + (PATH_MAX * 2) + 80];
if (dargs->optflag & (DA_MAPS_ONLY | DA_ALLOC_ONLY))
return (2);
if (dargs->optflag & DA_FORCE)
return (2);
if (dargs->optflag & DA_ADD) {
stdtype = da_std_type(dargs, defname);
is_allocatable = allocatable(dargs);
}
setdmapent();
while ((devmapp = getdmapent()) != NULL) {
suffix = DA_MAX_DEVNO + 1;
if ((rc = dmap_matchtype(devmapp, dargs->devinfo->devtype))
== 1) {
if (dargs->optflag & DA_REMOVE) {
if ((devmapp->dmap_devarray == NULL) ||
(devmapp->dmap_devarray[0] == NULL)) {
freedmapent(devmapp);
enddmapent();
return (2);
}
realname = dmap_physname(devmapp);
if (realname == NULL) {
freedmapent(devmapp);
enddmapent();
return (2);
}
if (strstr(realname, dargs->devinfo->devlist)
!= NULL) {
if (dargs->devinfo->devname != NULL &&
(dargs->optflag & DA_EVENT) != 0)
free(dargs->devinfo->devname);
dargs->devinfo->devname =
strdup(devmapp->dmap_devname);
found = 1;
freedmapent(devmapp);
continue;
}
} else if (dargs->optflag & DA_ADD) {
rc = (dmap_exact_dev(devmapp,
dargs->devinfo->devlist, &suffix));
if (rc == 0) {
if ((suffix < DA_MAX_DEVNO &&
suffix != -1) && stdtype)
tmp_bitmap |=
(uint64_t)(1LL << suffix);
} else if ((rc == 1) && !is_allocatable) {
rc = 0;
} else {
(void) snprintf(errmsg, sizeof (errmsg),
"Cannot add %s on node %s",
dargs->devinfo->devtype,
devmapp->dmap_devname);
syslog(LOG_ERR, "%s", errmsg);
freedmapent(devmapp);
enddmapent();
return (2);
}
} else
return (2);
} else if ((dargs->optflag & DA_ADD) &&
(stdtype || is_allocatable) &&
dmap_exact_dev(devmapp, dargs->devinfo->devlist,
&suffix)) {
(void) snprintf(errmsg, sizeof (errmsg),
"Cannot add %s on node %s type %s",
dargs->devinfo->devtype,
devmapp->dmap_devname,
devmapp->dmap_devtype);
syslog(LOG_ERR, "%s", errmsg);
freedmapent(devmapp);
enddmapent();
return (2);
}
tmp_str = _dmap2strentry(devmapp);
if (tmp_str == NULL) {
freedmapent(devmapp);
enddmapent();
return (2);
}
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();
if ((dargs->optflag & DA_REMOVE) && !found)
return (0);
if (dargs->optflag & DA_ADD) {
int len;
for (tmp = 0; tmp <= DA_MAX_DEVNO; tmp++)
if (!(tmp_bitmap & (1LL << tmp)))
break;
if (tmp > DA_MAX_DEVNO)
return (2);
len = strlen(defname);
if (stdtype &&
(strncmp(dargs->devinfo->devname, defname, len) == 0)) {
(void) snprintf(new_devname, DA_MAXNAME + 1, "%s%u",
defname, tmp);
if (dargs->devinfo->devname != NULL &&
(dargs->optflag & DA_EVENT) != 0)
free(dargs->devinfo->devname);
dargs->devinfo->devname = strdup(new_devname);
}
}
setdaent();
while ((devallocp = getdaent()) != NULL) {
rc = da_match(devallocp, dargs);
if (rc == 1) {
if (dargs->optflag & DA_ADD) {
if (dargs->optflag & DA_EVENT) {
(void) snprintf(errmsg, sizeof (errmsg),
"%s and %s out of sync,"
"%s only in %s.",
DEVALLOC, DEVMAP,
devallocp->da_devname, DEVALLOC);
syslog(LOG_ERR, "%s", errmsg);
}
freedaent(devallocp);
enddaent();
return (2);
} else if (dargs->optflag & DA_REMOVE) {
freedaent(devallocp);
continue;
}
}
tmp_str = _da2strentry(dargs, devallocp);
if (tmp_str == NULL) {
freedaent(devallocp);
enddaent();
return (2);
}
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();
if (dargs->optflag & DA_REMOVE)
return (1);
return (0);
}
static int
_build_lists(da_args *dargs, strentry_t **head_devallocp,
strentry_t **head_devmapp)
{
int rc = 0;
int found = 0;
devalloc_t *devallocp;
devmap_t *devmapp;
strentry_t *tail_str;
strentry_t *tmp_str;
if (dargs->optflag & DA_MAPS_ONLY)
goto dmap_only;
setdaent();
while ((devallocp = getdaent()) != NULL) {
rc = da_match(devallocp, dargs);
if (rc == 0) {
tmp_str = _da2strentry(dargs, devallocp);
if (tmp_str == NULL) {
freedaent(devallocp);
enddaent();
return (2);
}
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;
}
} else if (rc == 1)
found = 1;
freedaent(devallocp);
}
enddaent();
dmap_only:
if (dargs->optflag & DA_ALLOC_ONLY)
return (rc);
rc = 0;
setdmapent();
while ((devmapp = getdmapent()) != NULL) {
rc = dm_match(devmapp, dargs);
if (rc == 0) {
tmp_str = _dmap2strentry(devmapp);
if (tmp_str == NULL) {
freedmapent(devmapp);
enddmapent();
return (2);
}
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();
if (dargs->optflag & DA_REMOVE)
return (found);
return (rc);
}
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);
}
}
static void
_write_device_allocate(char *odevalloc, FILE *dafp, strentry_t *head_devallocp)
{
int is_on = -1;
strentry_t *tmp_str, *old_str;
struct stat dastat;
(void) fseek(dafp, (off_t)0, SEEK_SET);
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);
old_str = tmp_str;
tmp_str = tmp_str->se_next;
free(old_str);
}
}
static void
_write_device_maps(FILE *dmfp, strentry_t *head_devmapp)
{
strentry_t *tmp_str, *old_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);
old_str = tmp_str;
tmp_str = tmp_str->se_next;
free(old_str);
}
}
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) +1)) != 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);
}
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) + 1))
!= 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);
}
int
_da_lock_devdb(char *rootdir)
{
int lockfd = -1;
int ret;
int count = 0;
int retry = 10;
int retry_sleep;
uint_t seed;
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)
return (-1);
(void) fchown(lockfd, DA_UID, DA_GID);
if (lseek(lockfd, (off_t)0, SEEK_SET) == -1) {
(void) close(lockfd);
return (-1);
}
errno = 0;
while (retry > 0) {
count++;
seed = (uint_t)gethrtime();
ret = lockf(lockfd, F_TLOCK, 0);
if (ret == 0) {
(void) utime(lockfile, NULL);
return (lockfd);
}
if ((errno != EACCES) && (errno != EAGAIN)) {
(void) close(lockfd);
return (-1);
}
retry--;
retry_sleep = rand_r(&seed)/((RAND_MAX + 2)/3) + count;
(void) sleep(retry_sleep);
errno = 0;
}
return (-1);
}
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+F";
} else if (flag & DA_RDONLY) {
oflag = DA_RDONLY;
fmode = "rF";
}
if ((lockfd = _da_lock_devdb(rootdir)) == -1)
return (-1);
if ((dafp == NULL) || (flag & DA_MAPS_ONLY))
goto dmap_only;
path[0] = '\0';
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';
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);
}
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);
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) {
str_found = 0;
nsize = dastat.st_size + actionlen + 1;
}
if ((nbuf = (char *)malloc(nsize + 1)) == NULL)
return (-1);
nbuf[0] = '\0';
(void) strcpy(nbuf, actionstr);
nlen = strlen(nbuf);
plen = nlen;
if (ptr && !str_found) {
nlen = plen + strlen(line) + 1;
len = snprintf(nbuf + plen, nlen - plen, "%s", line);
if (len >= nsize) {
free(nbuf);
return (-1);
}
plen += len;
}
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';
if (fwrite(nbuf, nsize, nitems, tafp) < nitems) {
free(nbuf);
return (-1);
}
free(nbuf);
return (0);
}
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);
}
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_defattrs(tmpfp, head_defent);
if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
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);
}
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;
char *dapathp = NULL, *dmpathp = NULL;
char apath[MAXPATHLEN], mpath[MAXPATHLEN];
char 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;
if (dargs->optflag & DA_ADD || dargs->optflag & DA_REMOVE) {
if (dargs->optflag & DA_ALLOC_ONLY ||
dargs->optflag & DA_MAPS_ONLY)
return (0);
}
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;
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);
}
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;
}
if (stat(dapathp, &dastat) == 0) {
if ((dargs->optflag & (DA_ADD| DA_EVENT)) &&
(!(dargs->optflag & DA_FORCE)))
rc = _rebuild_lists(dargs, &head_devallocp,
&head_devmapp);
else
rc = _build_lists(dargs, &head_devallocp,
&head_devmapp);
if (rc != 0 && rc != 1) {
(void) close(tafd);
(void) unlink(apathp);
(void) close(lockfd);
return (-1);
}
} else
rc = 0;
if ((dargs->optflag & DA_REMOVE) && (rc == 0)) {
(void) close(tafd);
(void) unlink(apathp);
(void) close(lockfd);
return (0);
}
_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);
}
if (head_devmapp != NULL)
_write_device_maps(tmfp, head_devmapp);
out:
if (dargs->optflag & DA_ADD && !(dargs->optflag & DA_NO_OVERRIDE)) {
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);
}
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 + 1];
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)
break;
}
if (nentry == NULL) {
if (dentry == NULL)
instance = 0;
else
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;
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;
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;
if (nentry->devinfo.devlist) {
plen = strlen(nentry->devinfo.devlist);
nlen = nlen + plen + 1;
} else {
plen = 0;
}
if ((nentry->devinfo.devlist =
(char *)realloc(nentry->devinfo.devlist, nlen)) == NULL) {
if (new_entry) {
free(nentry->devinfo.devname);
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) {
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);
}
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 != 0)
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 != 0) && (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) {
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) {
current = prev->next;
} else {
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);
}
int
da_rm_list_entry(devlist_t *dlist, char *link, int type, char *devname)
{
int retval = 0;
deventry_t **dentry, *current, *prev;
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 (*dentry == (deventry_t *)NULL)
return (0);
prev = NULL;
for (current = *dentry; current != NULL;
prev = current, current = current->next) {
if (strcmp(devname, current->devinfo.devname))
continue;
retval = 1;
break;
}
if (retval == 0)
return (0);
free(current->devinfo.devname);
if (current->devinfo.devlist != NULL)
free(current->devinfo.devlist);
if (current->devinfo.devopts != NULL)
free(current->devinfo.devopts);
if (prev == NULL)
*dentry = current->next;
else
prev->next = current->next;
free(current);
return (retval);
}
int
da_is_on()
{
return (getdaon());
}
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);
}
}