+
+static int pw_parse_body(const char *buf) {
+ int pos;
+ if(sscanf(buf,"%[^:]:%[^:]:%d:%d:%[^:]:%[^:]:%s\n",pw_name,pw_password,&pw_passwd.pw_uid,&pw_passwd.pw_gid,pw_gecos,pw_dir,pw_shell) < 7) return -1;
+ pw_passwd.pw_name = pw_name;
+ pw_passwd.pw_passwd = pw_password;
+ pw_passwd.pw_gecos = pw_gecos;
+ pw_passwd.pw_dir = pw_dir;
+ pw_passwd.pw_shell = pw_shell;
+ pw_passwd.pw_comment = "";
+ return 0;
+}
+
+struct group *getgrnam(const char *name) {
+ FILE *fp;
+ char buf[1024];
+
+ if((fp=fopen("/etc/group","r"))==NULL) return NULL;
+ while(fgets(buf,sizeof(buf),fp)) {
+ if(buf[0] == '#') continue;
+ if(gr_parse_body(buf) < 0) {
+ fclose(fp);
+ return NULL;
+ }
+ if(strcmp(name,gr_name)==0) {
+ fclose(fp);
+ return &gr_group;
+ }
+ }
+ fclose(fp);
+ return NULL;
+}
+
+struct group *getgrgid(gid_t gid) {
+ FILE *fp;
+ char buf[1024];
+
+ if((fp=fopen("/etc/group","r"))==NULL) return NULL;
+ while(fgets(buf,sizeof(buf),fp)) {
+ if(buf[0] == '#') continue;
+ if(gr_parse_body(buf) < 0) {
+ fclose(fp);
+ return NULL;
+ }
+ if(gid == gr_group.gr_gid) {
+ fclose(fp);
+ return &gr_group;
+ }
+ }
+ fclose(fp);
+ return NULL;
+}
+
+struct group *getgrent() {
+ char buf[1024];
+ if(group_fp == NULL) return NULL;
+ if(fgets(buf,sizeof(buf),group_fp) == NULL) return NULL;
+ if(buf[0] == '#') return getgrent();
+ if(gr_parse_body(buf) < 0) return NULL;
+ return &gr_group;
+}
+
+void setgrent() {
+ if(group_fp != NULL) fclose(group_fp);
+ group_fp = fopen("/etc/group","r");
+}
+
+void endgrent() {
+ if(group_fp != NULL) fclose(group_fp);
+ group_fp = NULL;
+}
+
+struct passwd *getpwnam(const char *name) {
+ FILE *fp;
+ char buf[1024];
+
+ if((fp=fopen("/etc/passwd","r"))==NULL) return NULL;
+ while(fgets(buf,sizeof(buf),fp)) {
+ if(buf[0] == '#') continue;
+ if(pw_parse_body(buf) < 0) {
+ fclose(fp);
+ return NULL;
+ }
+ if(strcmp(name,pw_name)==0) {
+ fclose(fp);
+ return &pw_passwd;
+ }
+ }
+ fclose(fp);
+ return NULL;
+}
+
+struct passwd *getpwuid(uid_t uid) {
+ FILE *fp;
+ char buf[1024];
+
+ if((fp=fopen("/etc/passwd","r"))==NULL) return NULL;
+ while(fgets(buf,sizeof(buf),fp)) {
+ if(buf[0] == '#') continue;
+ if(pw_parse_body(buf) < 0) {
+ fclose(fp);
+ return NULL;
+ }
+ if(uid == pw_passwd.pw_uid) {
+ fclose(fp);
+ return &pw_passwd;
+ }
+ }
+ fclose(fp);
+ return NULL;
+}
+
+struct passwd *getpwent() {
+ char buf[1024];
+ if(passwd_fp == NULL) return NULL;
+ if(fgets(buf,sizeof(buf),passwd_fp) == NULL) return NULL;
+ if(buf[0] == '#') return getpwent();
+ if(pw_parse_body(buf) < 0) return NULL;
+ return &pw_passwd;
+}
+
+void setpwent() {
+ if(passwd_fp != NULL) fclose(passwd_fp);
+ passwd_fp = fopen("/etc/group","r");
+}
+
+void endpwent() {
+ if(passwd_fp != NULL) fclose(passwd_fp);
+ passwd_fp = NULL;
+}
+
+char *getpass(const char *prompt) {
+ static char buf[1024];
+ int len = 0;
+ fputs(prompt,stderr);
+ fflush(stdout);
+ if(fgets(buf,sizeof(buf),stdin)!=NULL) {
+ len = strlen(buf);
+ if(buf[len-1] == '\n') len--;
+ }
+ fputc('\n',stderr);
+ buf[len] = '\0';
+ return buf;
+}
+
+/* Argh... newlib's asprintf is totally broken... */
+int vasprintf(char **ret, const char *fmt, va_list ap) {
+ int n;
+ char *p;
+ *ret = malloc(128); /* just guess for now */
+ if(!*ret) return -1;
+ n = vsnprintf(*ret,128,fmt,ap);
+ if(n < 128) {
+ return n;
+ } else {
+ p = realloc(*ret,n+1);
+ if(!p) { free(*ret); return -1; }
+ return vsprintf(*ret = p,fmt,ap);
+ }
+}
+
+// FIXME: This needs to be in a header
+char *getlogin() {
+ return getenv("USER");
+}
+
+
+/*
+ * Other People's Code
+ */
+
+/* FreeBSD's dirname/basename */
+
+/* FIXME: Put these in a header */
+
+/*
+ * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+char *
+dirname(path)
+const char *path;
+{
+ static char bname[MAXPATHLEN];
+ register const char *endp;
+
+ /* Empty or NULL string gets treated as "." */
+ if (path == NULL || *path == '\0') {
+ (void)strcpy(bname, ".");
+ return(bname);
+ }
+
+ /* Strip trailing slashes */
+ endp = path + strlen(path) - 1;
+ while (endp > path && *endp == '/')
+ endp--;
+
+ /* Find the start of the dir */
+ while (endp > path && *endp != '/')
+ endp--;
+
+ /* Either the dir is "/" or there are no slashes */
+ if (endp == path) {
+ (void)strcpy(bname, *endp == '/' ? "/" : ".");
+ return(bname);
+ } else {
+ do {
+ endp--;
+ } while (endp > path && *endp == '/');
+ }
+
+ if (endp - path + 2 > sizeof(bname)) {
+ errno = ENAMETOOLONG;
+ return(NULL);
+ }
+ (void)strncpy(bname, path, endp - path + 1);
+ bname[endp - path + 1] = '\0';
+ return(bname);
+}
+
+char *
+basename(path)
+const char *path;
+{
+ static char bname[MAXPATHLEN];
+ register const char *endp, *startp;
+
+ /* Empty or NULL string gets treated as "." */
+ if (path == NULL || *path == '\0') {
+ (void)strcpy(bname, ".");
+ return(bname);
+ }
+
+ /* Strip trailing slashes */
+ endp = path + strlen(path) - 1;
+ while (endp > path && *endp == '/')
+ endp--;
+
+ /* All slashes becomes "/" */
+ if (endp == path && *endp == '/') {
+ (void)strcpy(bname, "/");
+ return(bname);
+ }
+
+ /* Find the start of the base */
+ startp = endp;
+ while (startp > path && *(startp - 1) != '/')
+ startp--;
+
+ if (endp - startp + 2 > sizeof(bname)) {
+ errno = ENAMETOOLONG;
+ return(NULL);
+ }
+ (void)strncpy(bname, startp, endp - startp + 1);
+ bname[endp - startp + 1] = '\0';
+ return(bname);
+}
+
+/* FreeBSD's uname */
+int
+uname(name)
+struct utsname *name;
+{
+ int mib[2], rval;
+ size_t len;
+ char *p;
+ int oerrno;
+
+ rval = 0;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_OSTYPE;
+ len = sizeof(name->sysname);
+ oerrno = errno;
+ if (sysctl(mib, 2, &name->sysname, &len, NULL, 0) == -1) {
+ if(errno == ENOMEM)
+ errno = oerrno;
+ else
+ rval = -1;
+ }
+ name->sysname[sizeof(name->sysname) - 1] = '\0';
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_HOSTNAME;
+ len = sizeof(name->nodename);
+ oerrno = errno;
+ if (sysctl(mib, 2, &name->nodename, &len, NULL, 0) == -1) {
+ if(errno == ENOMEM)
+ errno = oerrno;
+ else
+ rval = -1;
+ }
+ name->nodename[sizeof(name->nodename) - 1] = '\0';
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_OSRELEASE;
+ len = sizeof(name->release);
+ oerrno = errno;
+ if (sysctl(mib, 2, &name->release, &len, NULL, 0) == -1) {
+ if(errno == ENOMEM)
+ errno = oerrno;
+ else
+ rval = -1;
+ }
+ name->release[sizeof(name->release) - 1] = '\0';
+
+ /* The version may have newlines in it, turn them into spaces. */
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_VERSION;
+ len = sizeof(name->version);
+ oerrno = errno;
+ if (sysctl(mib, 2, &name->version, &len, NULL, 0) == -1) {
+ if (errno == ENOMEM)
+ errno = oerrno;
+ else
+ rval = -1;
+ }
+ name->version[sizeof(name->version) - 1] = '\0';
+ for (p = name->version; len--; ++p) {
+ if (*p == '\n' || *p == '\t') {
+ if (len > 1)
+ *p = ' ';
+ else
+ *p = '\0';
+ }
+ }
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_MACHINE;
+ len = sizeof(name->machine);
+ oerrno = errno;
+ if (sysctl(mib, 2, &name->machine, &len, NULL, 0) == -1) {
+ if (errno == ENOMEM)
+ errno = oerrno;
+ else
+ rval = -1;
+ }
+ name->machine[sizeof(name->machine) - 1] = '\0';
+ return (rval);
+}
+
+/* FreeBSD's gethostname */
+int
+gethostname(name, namelen)
+char *name;
+int namelen;
+{
+ int mib[2];
+ size_t size;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_HOSTNAME;
+ size = namelen;
+ if (sysctl(mib, 2, name, &size, NULL, 0) == -1)
+ return (-1);
+ return (0);
+}
+
+/* FreeBSD's daemon() - modified for nestedvm */
+int
+daemon(nochdir, noclose)
+int nochdir, noclose;
+{
+ int fd;
+
+ switch (fork()) {
+ case -1:
+ return (-1);
+ case 0:
+ break;
+ default:
+ _exit(0);
+ }
+
+ if (setsid() == -1)
+ return (-1);
+
+ if (!nochdir)
+ (void)chdir("/");
+
+ if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+ (void)dup2(fd, STDIN_FILENO);
+ (void)dup2(fd, STDOUT_FILENO);
+ (void)dup2(fd, STDERR_FILENO);
+ if (fd > 2)
+ (void)close(fd);
+ }
+ return (0);
+}
+
+/* FreeBSD's inet_addr/inet_aton */
+
+/*
+* Check whether "cp" is a valid ASCII representation
+ * of an Internet address and convert to a binary address.
+ * Returns 1 if the address is valid, 0 if not.
+ * This replaces inet_addr, the return value from which
+ * cannot distinguish between failure and a local broadcast address.
+ */
+int
+inet_aton(cp, addr)
+register const char *cp;
+struct in_addr *addr;
+{
+ u_long parts[4];
+ in_addr_t val;
+ char *c;
+ char *endptr;
+ int gotend, n;
+
+ c = (char *)cp;
+ n = 0;
+ /*
+ * Run through the string, grabbing numbers until
+ * the end of the string, or some error
+ */
+ gotend = 0;
+ while (!gotend) {
+ errno = 0;
+ val = strtoul(c, &endptr, 0);
+
+ if (errno == ERANGE) /* Fail completely if it overflowed. */
+ return (0);
+
+ /*
+ * If the whole string is invalid, endptr will equal
+ * c.. this way we can make sure someone hasn't
+ * gone '.12' or something which would get past
+ * the next check.
+ */
+ if (endptr == c)
+ return (0);
+ parts[n] = val;
+ c = endptr;
+
+ /* Check the next character past the previous number's end */
+ switch (*c) {
+ case '.' :
+ /* Make sure we only do 3 dots .. */
+ if (n == 3) /* Whoops. Quit. */
+ return (0);
+ n++;
+ c++;
+ break;
+
+ case '\0':
+ gotend = 1;
+ break;
+
+ default:
+ if (isspace((unsigned char)*c)) {
+ gotend = 1;
+ break;
+ } else
+ return (0); /* Invalid character, so fail */
+ }
+
+ }
+
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+
+ switch (n) {
+ case 0: /* a -- 32 bits */
+ /*
+ * Nothing is necessary here. Overflow checking was
+ * already done in strtoul().
+ */
+ break;
+ case 1: /* a.b -- 8.24 bits */
+ if (val > 0xffffff || parts[0] > 0xff)
+ return (0);
+ val |= parts[0] << 24;
+ break;
+
+ case 2: /* a.b.c -- 8.8.16 bits */
+ if (val > 0xffff || parts[0] > 0xff || parts[1] > 0xff)
+ return (0);
+ val |= (parts[0] << 24) | (parts[1] << 16);
+ break;
+
+ case 3: /* a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xff || parts[0] > 0xff || parts[1] > 0xff ||
+ parts[2] > 0xff)
+ return (0);
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ break;
+ }
+
+ if (addr != NULL)
+ addr->s_addr = htonl(val);
+ return (1);
+}
+
+/*
+ * ASCII internet address interpretation routine.
+ * The value returned is in network order.
+ */
+in_addr_t /* XXX should be struct in_addr :( */
+inet_addr(cp)
+register const char *cp;
+{
+ struct in_addr val;
+
+ if (inet_aton(cp, &val))
+ return (val.s_addr);
+ return (INADDR_NONE);
+}
+
+int
+getgrouplist(uname, agroup, groups, grpcnt)
+const char *uname;
+gid_t agroup;
+register gid_t *groups;
+int *grpcnt;
+{
+ register struct group *grp;
+ register int i, ngroups;
+ int ret, maxgroups;
+
+ ret = 0;
+ ngroups = 0;
+ maxgroups = *grpcnt;
+ /*
+ * When installing primary group, duplicate it;
+ * the first element of groups is the effective gid
+ * and will be overwritten when a setgid file is executed.
+ */
+ groups[ngroups++] = agroup;
+ if (maxgroups > 1)
+ groups[ngroups++] = agroup;
+ /*
+ * Scan the group file to find additional groups.
+ */
+ setgrent();
+ while ((grp = getgrent())) {
+ for (i = 0; i < ngroups; i++) {
+ if (grp->gr_gid == groups[i])
+ goto skip;
+ }
+ for (i = 0; grp->gr_mem[i]; i++) {
+ if (!strcmp(grp->gr_mem[i], uname)) {
+ if (ngroups >= maxgroups) {
+ ret = -1;
+ break;
+ }
+ groups[ngroups++] = grp->gr_gid;
+ break;
+ }
+ }
+skip: ;
+ }
+ endgrent();
+ *grpcnt = ngroups;
+ return (ret);
+}
+
+int
+initgroups(uname, agroup)
+const char *uname;
+gid_t agroup;
+{
+ gid_t groups[32], ngroups;
+
+ ngroups = 32;
+ getgrouplist(uname, agroup, groups, &ngroups);
+ return (setgroups(ngroups, groups));
+}