<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv=Content-Type content="text/html; charset=utf8">
<title>/usr/web/sources/contrib/nemo/libsys.c - Plan 9 from Bell Labs</title>
<!-- THIS FILE IS AUTOMATICALLY GENERATED. -->
<!-- EDIT sources.tr INSTEAD. -->
</meta>
</head>
<body>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"><a href="/plan9/">Plan 9 from Bell Labs</a>&rsquo;s /usr/web/sources/contrib/nemo/libsys.c</span></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
Copyright © 2009 Alcatel-Lucent.<br />
Distributed under the
<a href="/plan9/license.html">Lucent Public License version 1.02</a>.
<br />
<a href="/plan9/download.html">Download the Plan 9 distribution.</a>
</font>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<table width="100%" cellspacing=0 border=0><tr><td align="center">
<table cellspacing=0 cellpadding=5 bgcolor="#eeeeff"><tr><td align="left">
<pre>
<!-- END HEADER -->
#include "common.h"
#include &lt;auth.h&gt;
#include &lt;ndb.h&gt;

/*
*  number of predefined fd's
*/
int nsysfile=3;

static char err[Errlen];

/*
*  return the date
*/
extern char *
thedate(void)
{
       static char now[64];
       char *cp;

       strcpy(now, ctime(time(0)));
       cp = strchr(now, '\n');
       if(cp)
               *cp = 0;
       return now;
}

/*
*  return the user id of the current user
*/
extern char *
getlog(void)
{
       static char user[64];
       int fd;
       int n;

       fd = open("/dev/user", 0);
       if(fd &lt; 0)
               return nil;
       if((n=read(fd, user, sizeof(user)-1)) &lt;= 0)
               return nil;
       close(fd);
       user[n] = 0;
       return user;
}

/*
*  return the lock name (we use one lock per directory)
*/
static String *
lockname(char *path)
{
       String *lp;
       char *cp;

       /*
        *  get the name of the lock file
        */
       lp = s_new();
       cp = strrchr(path, '/');
       if(cp == nil){
               s_append(lp, "L.");
               s_append(lp, path);
       }else{
               s_nappend(lp, path, cp - path + 1);
               s_append(lp, "L.");
               s_append(lp, cp+1);
       }
       return lp;
}

int
syscreatelocked(char *path, int mode, int perm)
{
       return create(path, mode, DMEXCL|perm);
}

int
sysopenlocked(char *path, int mode)
{
/*      return open(path, OEXCL|mode);/**/
       return open(path, mode);                /* until system call is fixed */
}

int
sysunlockfile(int fd)
{
       return close(fd);
}

/*
*  try opening a lock file.  If it doesn't exist try creating it.
*/
static int
openlockfile(Mlock *l)
{
       int fd;
       Dir *d;

       fd = open(s_to_c(l-&gt;name), OREAD);
       if(fd &gt;= 0){
               l-&gt;fd = fd;
               return 0;
       }

       d = dirstat(s_to_c(l-&gt;name));
       if(d == nil){
               /* file doesn't exist */
               /* try creating it */
               l-&gt;fd = create(s_to_c(l-&gt;name), OREAD, DMEXCL|0666);
               if(l-&gt;fd &lt; 0)
                       syslog(0, "mail", "lock error: %s: %r", s_to_c(l-&gt;name));
               return 0;
       } else
               free(d);

       return 1; /* try again later */
}

#define LSECS 5*60

/*
*  Set a lock for a particular file.  The lock is a file in the same directory
*  and has L. prepended to the name of the last element of the file name.
*/
extern Mlock *
syslock(char *path)
{
       Mlock *l;
       int tries;

       l = mallocz(sizeof(Mlock), 1);
       if(l == 0)
               return nil;

       l-&gt;name = lockname(path);

       /*
        *  wait LSECS seconds for it to unlock
        */
       for(tries = 0; tries &lt; LSECS*2; tries++){
               switch(openlockfile(l)){
               case 0:
                       return l;
               case 1:
                       sleep(500);
                       break;
               default:
                       goto noway;
               }
       }

noway:
       s_free(l-&gt;name);
       free(l);
       return nil;
}

/*
*  like lock except don't wait
*/
extern Mlock *
trylock(char *path)
{
       Mlock *l;
       char buf[1];
       int fd;

       l = malloc(sizeof(Mlock));
       if(l == 0)
               return 0;

       l-&gt;name = lockname(path);
       if(openlockfile(l) != 0){
               s_free(l-&gt;name);
               free(l);
               return 0;
       }

       /* fork process to keep lock alive */
       switch(l-&gt;pid = rfork(RFPROC)){
       default:
               break;
       case 0:
               fd = l-&gt;fd;
               for(;;){
                       sleep(1000*60);
                       if(pread(fd, buf, 1, 0) &lt; 0)
                               break;
               }
               _exits(0);
       }
       return l;
}

extern void
syslockrefresh(Mlock *l)
{
       char buf[1];

       pread(l-&gt;fd, buf, 1, 0);
}

extern void
sysunlock(Mlock *l)
{
       if(l == 0)
               return;
       if(l-&gt;name){
               s_free(l-&gt;name);
       }
       if(l-&gt;fd &gt;= 0)
               close(l-&gt;fd);
       if(l-&gt;pid &gt; 0)
               postnote(PNPROC, l-&gt;pid, "time to die");
       free(l);
}

/*
*  Open a file.  The modes are:
*
*      l       - locked
*      a       - set append permissions
*      r       - readable
*      w       - writable
*      A       - append only (doesn't exist in Bio)
*/
extern Biobuf *
sysopen(char *path, char *mode, ulong perm)
{
       int sysperm;
       int sysmode;
       int fd;
       int docreate;
       int append;
       int truncate;
       Dir *d, nd;
       Biobuf *bp;

       /*
        *  decode the request
        */
       sysperm = 0;
       sysmode = -1;
       docreate = 0;
       append = 0;
       truncate = 0;
       for(; mode &amp;&amp; *mode; mode++)
               switch(*mode){
               case 'A':
                       sysmode = OWRITE;
                       append = 1;
                       break;
               case 'c':
                       docreate = 1;
                       break;
               case 'l':
                       sysperm |= DMEXCL;
                       break;
               case 'a':
                       sysperm |= DMAPPEND;
                       break;
               case 'w':
                       if(sysmode == -1)
                               sysmode = OWRITE;
                       else
                               sysmode = ORDWR;
                       break;
               case 'r':
                       if(sysmode == -1)
                               sysmode = OREAD;
                       else
                               sysmode = ORDWR;
                       break;
               case 't':
                       truncate = 1;
                       break;
               default:
                       break;
               }
       switch(sysmode){
       case OREAD:
       case OWRITE:
       case ORDWR:
               break;
       default:
               if(sysperm&amp;DMAPPEND)
                       sysmode = OWRITE;
               else
                       sysmode = OREAD;
               break;
       }

       /*
        *  create file if we need to
        */
       if(truncate)
               sysmode |= OTRUNC;
       fd = open(path, sysmode);
       if(fd &lt; 0){
               d = dirstat(path);
               if(d == nil){
                       if(docreate == 0)
                               return 0;

                       fd = create(path, sysmode, sysperm|perm);
                       if(fd &lt; 0)
                               return 0;
                       nulldir(&amp;nd);
                       nd.mode = sysperm|perm;
                       dirfwstat(fd, &amp;nd);
               } else {
                       free(d);
                       return 0;
               }
       }

       bp = (Biobuf*)malloc(sizeof(Biobuf));
       if(bp == 0){
               close(fd);
               return 0;
       }
       memset(bp, 0, sizeof(Biobuf));
       Binit(bp, fd, sysmode&amp;~OTRUNC);

       if(append)
               Bseek(bp, 0, 2);
       return bp;
}

/*
*  close the file, etc.
*/
int
sysclose(Biobuf *bp)
{
       int rv;

       rv = Bterm(bp);
       close(Bfildes(bp));
       free(bp);
       return rv;
}

/*
*  create a file
*/
int
syscreate(char *file, int mode, ulong perm)
{
       return create(file, mode, perm);
}

/*
*  make a directory
*/
int
sysmkdir(char *file, ulong perm)
{
       int fd;

       if((fd = create(file, OREAD, DMDIR|perm)) &lt; 0)
               return -1;
       close(fd);
       return 0;
}

/*
*  change the group of a file
*/
int
syschgrp(char *file, char *group)
{
       Dir nd;

       if(group == 0)
               return -1;
       nulldir(&amp;nd);
       nd.gid = group;
       return dirwstat(file, &amp;nd);
}

extern int
sysdirreadall(int fd, Dir **d)
{
       return dirreadall(fd, d);
}

/*
*  read in the system name
*/
extern char *
sysname_read(void)
{
       static char name[128];
       char *cp;

       cp = getenv("site");
       if(cp == 0 || *cp == 0)
               cp = alt_sysname_read();
       if(cp == 0 || *cp == 0)
               cp = "kremvax";
       strecpy(name, name+sizeof name, cp);
       return name;
}
extern char *
alt_sysname_read(void)
{
       static char name[128];
       int n, fd;

       fd = open("/dev/sysname", OREAD);
       if(fd &lt; 0)
               return 0;
       n = read(fd, name, sizeof(name)-1);
       close(fd);
       if(n &lt;= 0)
               return 0;
       name[n] = 0;
       return name;
}

/*
*  get all names
*/
extern char**
sysnames_read(void)
{
       static char **namev;
       Ndbtuple *t, *nt;
       int n;
       char *cp;

       if(namev)
               return namev;

       free(csgetvalue(0, "sys", alt_sysname_read(), "dom", &amp;t));

       n = 0;
       for(nt = t; nt; nt = nt-&gt;entry)
               if(strcmp(nt-&gt;attr, "dom") == 0)
                       n++;

       namev = (char**)malloc(sizeof(char *)*(n+3));

       if(namev){
               n = 0;
               namev[n++] = strdup(sysname_read());
               cp = alt_sysname_read();
               if(cp)
                       namev[n++] = strdup(cp);
               for(nt = t; nt; nt = nt-&gt;entry)
                       if(strcmp(nt-&gt;attr, "dom") == 0)
                               namev[n++] = strdup(nt-&gt;val);
               namev[n] = 0;
       }
       if(t)
               ndbfree(t);

       return namev;
}

/*
*  read in the domain name
*/
extern char *
domainname_read(void)
{
       char **namev;

       for(namev = sysnames_read(); *namev; namev++)
               if(strchr(*namev, '.'))
                       return *namev;
       return 0;
}

/*
*  return true if the last error message meant file
*  did not exist.
*/
extern int
e_nonexistent(void)
{
       rerrstr(err, sizeof(err));
       return strcmp(err, "file does not exist") == 0;
}

/*
*  return true if the last error message meant file
*  was locked.
*/
extern int
e_locked(void)
{
       rerrstr(err, sizeof(err));
       return strcmp(err, "open/create -- file is locked") == 0;
}

/*
*  return the length of a file
*/
extern long
sysfilelen(Biobuf *fp)
{
       Dir *d;
       long rv;

       d = dirfstat(Bfildes(fp));
       if(d == nil)
               return -1;
       rv = d-&gt;length;
       free(d);
       return rv;
}

/*
*  remove a file
*/
extern int
sysremove(char *path)
{
       return remove(path);
}

/*
*  rename a file, fails unless both are in the same directory
*/
extern int
sysrename(char *old, char *new)
{
       Dir d;
       char *obase;
       char *nbase;

       obase = strrchr(old, '/');
       nbase = strrchr(new, '/');
       if(obase){
               if(nbase == 0)
                       return -1;
               if(strncmp(old, new, obase-old) != 0)
                       return -1;
               nbase++;
       } else {
               if(nbase)
                       return -1;
               nbase = new;
       }
       nulldir(&amp;d);
       d.name = nbase;
       return dirwstat(old, &amp;d);
}

/*
*  see if a file exists
*/
extern int
sysexist(char *file)
{
       Dir     *d;

       d = dirstat(file);
       if(d == nil)
               return 0;
       free(d);
       return 1;
}

/*
*  return nonzero if file is a directory
*/
extern int
sysisdir(char *file)
{
       Dir     *d;
       int     rv;

       d = dirstat(file);
       if(d == nil)
               return 0;
       rv = d-&gt;mode &amp; DMDIR;
       free(d);
       return rv;
}

/*
* kill a process or process group
*/

static int
stomp(int pid, char *file)
{
       char name[64];
       int fd;

       snprint(name, sizeof(name), "/proc/%d/%s", pid, file);
       fd = open(name, 1);
       if(fd &lt; 0)
               return -1;
       if(write(fd, "die: yankee pig dog\n", sizeof("die: yankee pig dog\n") - 1) &lt;= 0){
               close(fd);
               return -1;
       }
       close(fd);
       return 0;

}

/*
*  kill a process
*/
extern int
syskill(int pid)
{
       return stomp(pid, "note");

}

/*
*  kill a process group
*/
extern int
syskillpg(int pid)
{
       return stomp(pid, "notepg");
}

extern int
sysdetach(void)
{
       if(rfork(RFENVG|RFNAMEG|RFNOTEG) &lt; 0) {
               werrstr("rfork failed");
               return -1;
       }
       return 0;
}

/*
*  catch a write on a closed pipe
*/
static int *closedflag;
static int
catchpipe(void *a, char *msg)
{
       static char *foo = "sys: write on closed pipe";

       USED(a);
       if(strncmp(msg, foo, strlen(foo)) == 0){
               if(closedflag)
                       *closedflag = 1;
               return 1;
       }
       return 0;
}
void
pipesig(int *flagp)
{
       closedflag = flagp;
       atnotify(catchpipe, 1);
}
void
pipesigoff(void)
{
       atnotify(catchpipe, 0);
}

void
exit(int i)
{
       char buf[32];

       if(i == 0)
               exits(0);
       snprint(buf, sizeof(buf), "%d", i);
       exits(buf);
}

static int
islikeatty(int fd)
{
       char buf[64];

       if(fd2path(fd, buf, sizeof buf) != 0)
               return 0;

       /* might be /mnt/term/dev/cons */
       return strlen(buf) &gt;= 9 &amp;&amp; strcmp(buf+strlen(buf)-9, "/dev/cons") == 0;
}

extern int
holdon(void)
{
       int fd;

       if(!islikeatty(0))
               return -1;

       fd = open("/dev/consctl", OWRITE);
       write(fd, "holdon", 6);

       return fd;
}

extern int
sysopentty(void)
{
       return open("/dev/cons", ORDWR);
}

extern void
holdoff(int fd)
{
       write(fd, "holdoff", 7);
       close(fd);
}

extern int
sysfiles(void)
{
       return 128;
}

/*
*  expand a path relative to the user's mailbox directory
*
*  if the path starts with / or ./, don't change it
*
*/
extern String *
mboxpath(char *path, char *user, String *to, int dot)
{
       if (dot || *path=='/' || strncmp(path, "./", 2) == 0
                             || strncmp(path, "../", 3) == 0) {
               to = s_append(to, path);
       } else {
               to = s_append(to, MAILROOT);
               to = s_append(to, "/box/");
               to = s_append(to, user);
               to = s_append(to, "/");
               to = s_append(to, path);
       }
       return to;
}

extern String *
mboxname(char *user, String *to)
{
       return mboxpath("mbox", user, to, 0);
}

extern String *
deadletter(String *to)          /* pass in sender??? */
{
       char *cp;

       cp = getlog();
       if(cp == 0)
               return 0;
       return mboxpath("dead.letter", cp, to, 0);
}

char *
homedir(char *user)
{
       USED(user);
       return getenv("home");
}

String *
readlock(String *file)
{
       char *cp;

       cp = getlog();
       if(cp == 0)
               return 0;
       return mboxpath("reading", cp, file, 0);
}

String *
username(String *from)
{
       int n;
       Biobuf *bp;
       char *p, *q;
       String *s;

       bp = Bopen("/adm/keys.who", OREAD);
       if(bp == 0)
               bp = Bopen("/adm/netkeys.who", OREAD);
       if(bp == 0)
               return 0;

       s = 0;
       n = strlen(s_to_c(from));
       for(;;) {
               p = Brdline(bp, '\n');
               if(p == 0)
                       break;
               p[Blinelen(bp)-1] = 0;
               if(strncmp(p, s_to_c(from), n))
                       continue;
               p += n;
               if(*p != ' ' &amp;&amp; *p != '\t')     /* must be full match */
                       continue;
               while(*p &amp;&amp; (*p == ' ' || *p == '\t'))
                               p++;
               if(*p == 0)
                       continue;
               for(q = p; *q; q++)
                       if(('0' &lt;= *q &amp;&amp; *q &lt;= '9') || *q == '&lt;')
                               break;
               while(q &gt; p &amp;&amp; q[-1] != ' ' &amp;&amp; q[-1] != '\t')
                       q--;
               while(q &gt; p &amp;&amp; (q[-1] == ' ' || q[-1] == '\t'))
                       q--;
               *q = 0;
               s = s_new();
               s_append(s, "\"");
               s_append(s, p);
               s_append(s, "\"");
               break;
       }
       Bterm(bp);
       return s;
}

char *
remoteaddr(int fd, char *dir)
{
       char buf[128], *p;
       int n;

       if(dir == 0){
               if(fd2path(fd, buf, sizeof(buf)) != 0)
                       return "";

               /* parse something of the form /net/tcp/nnnn/data */
               p = strrchr(buf, '/');
               if(p == 0)
                       return "";
               strncpy(p+1, "remote", sizeof(buf)-(p-buf)-2);
       } else
               snprint(buf, sizeof buf, "%s/remote", dir);
       buf[sizeof(buf)-1] = 0;

       fd = open(buf, OREAD);
       if(fd &lt; 0)
               return "";
       n = read(fd, buf, sizeof(buf)-1);
       close(fd);
       if(n &gt; 0){
               buf[n] = 0;
               p = strchr(buf, '!');
               if(p)
                       *p = 0;
               return strdup(buf);
       }
       return "";
}

//  create a file and
//      1) ensure the modes we asked for
//      2) make gid == uid
static int
docreate(char *file, int perm)
{
       int fd;
       Dir ndir;
       Dir *d;

       //  create the mbox
       fd = create(file, OREAD, perm);
       if(fd &lt; 0){
               fprint(2, "couldn't create %s\n", file);
               return -1;
       }
       d = dirfstat(fd);
       if(d == nil){
               fprint(2, "couldn't stat %s\n", file);
               return -1;
       }
       nulldir(&amp;ndir);
       ndir.mode = perm;
       ndir.gid = d-&gt;uid;
       if(dirfwstat(fd, &amp;ndir) &lt; 0)
               fprint(2, "couldn't chmod %s: %r\n", file);
       close(fd);
       return 0;
}

//  create a mailbox
int
creatembox(char *user, char *folder)
{
       char *p;
       String *mailfile;
       char buf[512];
       Mlock *ml;

       mailfile = s_new();
       if(folder == 0)
               mboxname(user, mailfile);
       else {
               snprint(buf, sizeof(buf), "%s/mbox", folder);
               mboxpath(buf, user, mailfile, 0);
       }

       // don't destroy existing mailbox
       if(access(s_to_c(mailfile), 0) == 0){
               fprint(2, "mailbox already exists\n");
               return -1;
       }
       fprint(2, "creating new mbox: %s\n", s_to_c(mailfile));

       //  make sure preceding levels exist
       for(p = s_to_c(mailfile); p; p++) {
               if(*p == '/')   /* skip leading or consecutive slashes */
                       continue;
               p = strchr(p, '/');
               if(p == 0)
                       break;
               *p = 0;
               if(access(s_to_c(mailfile), 0) != 0){
                       if(docreate(s_to_c(mailfile), DMDIR|0711) &lt; 0)
                               return -1;
               }
               *p = '/';
       }

       //  create the mbox
       if(docreate(s_to_c(mailfile), 0622|DMAPPEND|DMEXCL) &lt; 0)
               return -1;

       /*
        *  create the lock file if it doesn't exist
        */
       ml = trylock(s_to_c(mailfile));
       if(ml != nil)
               sysunlock(ml);

       return 0;
}
<!-- BEGIN TAIL -->
</pre>
</td></tr></table>
</td></tr></table>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"></span></p>
<p style="margin-top: 0; margin-bottom: 0.50in"></p>
<p style="margin-top: 0; margin-bottom: 0.33in"></p>
<center><table border="0"><tr>
<td valign="middle"><a href="http://www.alcatel-lucent.com/"><img border="0" src="/plan9/img/logo_ft.gif" alt="Bell Labs" />
</a></td>
<td valign="middle"><a href="http://www.opensource.org"><img border="0" alt="OSI certified" src="/plan9/img/osi-certified-60x50.gif" />
</a></td>
<td><img style="padding-right: 45px;" alt="Powered by Plan 9" src="/plan9/img/power36.gif" />
</td>
</tr></table></center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center>
<span style="font-size: 10pt">(<a href="/plan9/">Return to Plan 9 Home Page</a>)</span>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
<span style="font-size: 10pt"><a href="http://www.lucent.com/copyright.html">Copyright</a></span>
<span style="font-size: 10pt">© 2009 Alcatel-Lucent.</span>
<span style="font-size: 10pt">All Rights Reserved.</span>
<br />
<span style="font-size: 10pt">Comments to</span>
<span style="font-size: 10pt"><a href="mailto:[email protected]">[email protected]</a>.</span>
</font></center>
</body>
</html>