home *** CD-ROM | disk | FTP | other *** search
/ ftp.freefriends.org / ftp.freefriends.org.tar / ftp.freefriends.org / arnold / Source / mush.rstevens.tar.gz / mush.tar / lock.c < prev    next >
C/C++ Source or Header  |  1992-10-30  |  11KB  |  468 lines

  1. /*
  2.  * lock.c -- deal with file locking on various architectures and UNIXs.
  3.  * dot_lock() creates a file with the same name as the parameter passed
  4.  * with the appendage ".lock" -- this is to be compatible with certain
  5.  * systems that don't use flock or lockf or whatever they have available
  6.  * that they don't use.
  7.  */
  8.  
  9. #ifdef USG
  10. #include <unistd.h>
  11. #endif /* USG */
  12. #include "mush.h"
  13. #if defined(SYSV) && !defined(USG)
  14. #include <sys/locking.h>
  15. #endif /* SYSV && !USG */
  16.  
  17. #ifdef DOT_LOCK
  18.  
  19. #ifndef DOLOCK_PATH
  20. #define DOLOCK_PATH    "dotlock"    /* Path to the executable */
  21. #endif /* DOLOCK_PATH */
  22. #ifndef DOLOCK_NAME
  23. #define DOLOCK_NAME    "dotlock"    /* Name of the executable */
  24. #endif /* DOLOCK_NAME */
  25. #define DOLOCKIT    "-l"
  26. #define UNLOCKIT    "-u"
  27.  
  28. #ifdef LOCK_PROG        /* Define for standalone locking program */
  29.  
  30. /* System V Release 2 does not support saved-set-group-id, so it is not
  31.  * sufficient for mush to be setgid mail (or whatever group has write
  32.  * permission on /usr/mail).  Instead, we need an external program that
  33.  * can be setgid, which mush then runs to create and remove lock files.
  34.  * Compiling this file with -DDOT_LOCK -DLOCK_PROG added to your CFLAGS
  35.  * will generate such a program:
  36.  *
  37.  *    cc -o dotlock -DDOT_LOCK -DLOCK_PROG lock.c xcreat.c
  38.  *
  39.  * For mush purposes, you should hardwire the DOLOCK_PATH to the full path
  40.  * name of the installed executable.  This helps prevent malicious users
  41.  * from substituting a different program.
  42.  *
  43.  * The program generated can also be used to perform mail locking from
  44.  * shell scripts, so you may wish to consider installing it (or a copy)
  45.  * in a publicly accessible location, e.g. /usr/local/bin.  Note that
  46.  * this program is not compatible with Xenix mail locking!
  47.  */
  48.  
  49. #undef    on_intr()
  50. #define    on_intr()    0
  51. #undef    off_intr()
  52. #define    off_intr()    0
  53. #undef    print
  54. #define    print        printf
  55. #undef    print_more
  56. #define    print_more    fflush(stdout), printf
  57. #undef    error
  58. #define    error        printf
  59. #undef    sprintf
  60.  
  61. /* Simple dotlock program.  Exits 0 for success, nonzero for failure.
  62.  *
  63.  * Usage:
  64.  *    dotlock -lock filename
  65.  *    dotlock -unlock filename
  66.  *
  67.  * The options can be abbreviated to their first letter (-l or -u);
  68.  * any other usage fails silently.
  69.  */
  70.  
  71. main(argc, argv)
  72. int argc;
  73. char **argv;
  74. {
  75.     char *myname = rindex(argv[0], '/');
  76.  
  77.     if (!myname)
  78.     myname = argv[0];
  79.     if (strcmp(myname, DOLOCK_NAME) != 0 || argc != 3) 
  80.     exit(-1);
  81.     
  82.     if (strncmp(argv[1], DOLOCKIT, 2) == 0)
  83.     dot_lock(argv[2]);
  84.     else if (strncmp(argv[1], UNLOCKIT, 2) == 0)
  85.     dot_unlock(argv[2]);
  86.     exit(-1);
  87. }
  88.  
  89. #else /* !LOCK_PROG */
  90.  
  91. extern int sgid;
  92. #ifdef BSD
  93. extern int rgid;
  94. #endif /* BSD */
  95.  
  96. #ifdef SVR2
  97. /* No saved-setgid, so fork just long enough to create the lockfile. */
  98. lock_proc(filename, how)
  99. char *filename, *how;
  100. {
  101.     int kid, pid, status;
  102.  
  103.     errno = 0;
  104.     switch (kid = fork()) {
  105.     case 0:
  106.         execle(DOLOCK_PATH, DOLOCK_NAME, how, filename, NULL, environ);
  107.         return kid;
  108.     case -1:
  109.         error("Unable to fork to change group id");
  110.         return -1;
  111.     default:
  112.         /* For SYSV, we're not doing SIGCLD handling, so go ahead
  113.          * and reap everything in sight until we get the one we want.
  114.          */
  115.         while ((pid = wait(&status)) != -1 && pid != kid)
  116.         ;
  117.         if (pid == kid)
  118.         errno = ((status & 0xf) == 0) ? (status >> 8) & 0xf : 0
  119.         return errno ? -1 : 0;
  120.     }
  121. }
  122. #endif /* SVR2 */
  123.  
  124. #endif /* LOCK_PROG */
  125.  
  126. dot_lock(filename)
  127. char *filename;
  128. {
  129.     char buf[MAXPATHLEN];
  130.     int lockfd, cnt = 0;
  131.     SIGRET (*oldint)(), (*oldquit)();
  132.  
  133. #ifndef LOCK_PROG
  134. #if defined(SYSV) && !defined(HPUX) && !defined(IRIX4)
  135.     /* Only the spoolfile needs to be dot_locked -- other files are
  136.      * handled by lock_fopen, below.  To avoid collisions with 14-char
  137.      * file name limits, we allow dot_locking ONLY of the spoolfile.
  138.      */
  139.     if (strcmp(spoolfile, filename) != 0)
  140.     return 0;
  141. #ifdef SVR2
  142.     return lock_proc(filename, DOLOCKIT);
  143. #endif /* SVR2 */
  144. #endif /* SYSV && !HPUX && !IRIX4 */
  145. #ifdef BSD
  146.     setregid(rgid, sgid);
  147. #else /* BSD */
  148.     setgid(sgid);
  149. #endif /* BSD */
  150. #endif /* !LOCK_PROG */
  151. #ifdef M_XENIX
  152.     (void) sprintf(buf, "/tmp/%.10s.mlk", login);
  153. #else /* M_XENIX */
  154.     (void) sprintf(buf, "%s.lock", filename);
  155. #endif /* M_XENIX */
  156.     on_intr();
  157.     while ((lockfd = xcreat(buf, 0444)) == -1) {
  158.     if (errno != EEXIST) {
  159.         error("unable to lock %s", filename);
  160.         break;
  161.     }
  162.     if (cnt++ == 0)
  163.         print("%s already locked, waiting", filename);
  164.     else
  165.         print_more(".");
  166.     sleep(1);
  167.     if (ison(glob_flags, WAS_INTR)) {
  168.         print_more("\nAborted.\n");
  169.         break;
  170.     }
  171.     }
  172.     off_intr();
  173.     if (lockfd != -1) {
  174.     if (cnt)
  175.         print("done.\n");
  176.     (void) close(lockfd);
  177.     }
  178. #ifdef LOCK_PROG
  179.     if (lockfd < 0)
  180.     print_more("\n");
  181.     exit(lockfd < 0? errno : 0);
  182. #else /* !LOCK_PROG */
  183. #ifdef BSD
  184.     setregid(sgid, rgid);
  185. #else
  186.     setgid(getgid());
  187. #endif /* BSD */
  188.     return lockfd == -1? -1 : 0;
  189. #endif /* LOCK_PROG */
  190. }
  191.  
  192. dot_unlock(filename)
  193. char *filename;
  194. {
  195.     char buf[MAXPATHLEN], *p;
  196.  
  197. #if !defined(M_XENIX) || defined(LOCK_PROG)
  198.     (void) sprintf(buf, "%s.lock", filename);
  199. #ifndef LOCK_PROG
  200.     {
  201.     /* If the file was locked through open_file(), we may not have
  202.      * a complete pathname to work with here.  Expand it and test
  203.      * whether we need to unlink at all.  This should really be
  204.      * handled by having open_file() return the name it used, but
  205.      * that breaks too many other things at the moment.
  206.      */
  207.     int isdir = 0;
  208.     p = getpath(buf, &isdir);
  209.     if (isdir)
  210.         return 0;
  211.     (void) strcpy(buf, p);
  212.     }
  213. #if defined(SYSV)  && !defined(HPUX) && !defined(IRIX4)
  214.     if (strncmp(spoolfile, buf, strlen(spoolfile)) != 0)
  215.     return 0;
  216. #ifdef SVR2
  217.     p = rindex(buf, '.');
  218.     *p = 0;
  219.     return lock_proc(buf, UNLOCKIT);
  220. #endif /* SVR2 */
  221. #endif /* SYSV && !HPUX && !IRIX4 */
  222. #else /* LOCK_PROG */
  223.     errno = 0;
  224. #endif /* !LOCK_PROG */
  225. #else /* M_XENIX && !LOCK_PROG */
  226.     (void) sprintf(buf, "/tmp/%.10s.mlk", login);
  227. #endif /* !M_XENIX || LOCK_PROG */
  228. #ifndef LOCK_PROG
  229. #ifdef BSD
  230.     setregid(rgid, sgid);
  231. #else /* BSD */
  232.     setgid(sgid);
  233. #endif /* BSD */
  234. #endif /* !LOCK_PROG */
  235.     (void) unlink(buf);
  236. #ifdef LOCK_PROG
  237.     exit(errno);
  238. #else /* !LOCK_PROG */
  239. #ifdef BSD
  240.     setregid(sgid, rgid);
  241. #else
  242.     setgid(getgid());
  243. #endif /* BSD */
  244. #endif /* LOCK_PROG */
  245.     return 0;
  246. }
  247. #endif /* DOT_LOCK */
  248.  
  249. #ifndef LOCK_PROG
  250.  
  251. #ifdef SYSV
  252.  
  253. /*
  254.  * Define some BSD names for the SYSV world
  255.  */
  256. #ifdef USG
  257. #define LOCK_SH F_RDLCK
  258. #define LOCK_EX F_WRLCK
  259. #define LOCK_UN F_UNLCK
  260. #else /* USG */
  261. #define LOCK_SH LK_LOCK
  262. #define LOCK_EX LK_LOCK
  263. #define LOCK_UN LK_UNLCK
  264. #endif /* USG */
  265. #define LOCK_NB 0    /* Always non-blocking in this case */
  266.  
  267. #ifdef EWOULDBLOCK
  268. #undef EWOULDBLOCK
  269. #endif /* EWOULDBLOCK */
  270. #define EWOULDBLOCK    EAGAIN
  271.  
  272. #ifndef F_SETLKW
  273. #define F_SETLKW F_SETLK
  274. #endif /* F_SETLKW */
  275.  
  276. flock(fd, op)
  277. int fd, op;
  278. {
  279. #ifndef USG
  280.     (void) locking(fd, op, 0); /* old xenix (sys III) */
  281.     return 0;
  282. #else
  283.     struct flock l;
  284.  
  285.     l.l_len = 0L;
  286.     l.l_start = 0L;
  287.     l.l_whence = 1;
  288.     l.l_type = op;
  289.  
  290.     return fcntl(fd, F_SETLKW, &l);
  291. #endif /* USG */
  292. }
  293.  
  294. #endif /* SYSV */
  295.  
  296. static struct options *exclude_list;
  297.  
  298. /* Quick'n'dirty test to avoid opening the same file multiple times.
  299.  * Fails if we aren't passed full paths or if the file is known by
  300.  * more than one name, but you can't have everything.
  301.  */
  302. static FILE *
  303. exclusive_fopen(filename, mode)
  304. char *filename, *mode;
  305. {
  306.     struct options *tmp;
  307.     FILE *fp;
  308.     
  309.     for (tmp = exclude_list; tmp; tmp = tmp->next)
  310.     if (strcmp(tmp->option, filename) == 0) {
  311.         errno = EWOULDBLOCK;
  312.         return NULL_FILE;
  313.     }
  314. #ifdef DOT_LOCK
  315.     /* For additional assurance that each file is opened only once,
  316.      * and for ease of recovery from interrupts and errors, do the
  317.      * dot-locking here and unlock in exclusive_fclose().
  318.      */
  319.     if (dot_lock(filename) != 0)
  320.     return NULL_FILE;
  321. #endif /* DOT_LOCK */
  322.     if (!(fp = mask_fopen(filename, mode)))
  323.     return NULL_FILE;
  324.     if (tmp = (struct options *)malloc(sizeof(struct options))) {
  325.     tmp->option = savestr(filename);
  326.     tmp->value = (char *)fp;
  327.     /*
  328.      * NOTE: The LCKDFLDIR code below depends on this stackwise
  329.      * insertion to be able to close/reopen the file pointer.
  330.      * These routines therefore cannot cleanly be used outside
  331.      * of lock_fopen() and close_lock(), which handle LCKDFLDIR.
  332.      */
  333.     tmp->next = exclude_list;
  334.     exclude_list = tmp;
  335.     return fp;
  336.     } else
  337.     (void) fclose(fp);
  338.     return NULL_FILE;
  339. }
  340.  
  341. static int
  342. exclusive_fclose(fileptr)
  343. FILE *fileptr;
  344. {
  345.     struct options *tmp1, *tmp2;
  346.     int n = 0;
  347.     
  348.     for (tmp1 = tmp2 = exclude_list; tmp1; tmp2 = tmp1, tmp1 = tmp1->next)
  349.     if ((FILE *)(tmp1->value) == fileptr) {
  350.         if (tmp1 == tmp2)
  351.         exclude_list = tmp1->next;
  352.         else
  353.         tmp2->next = tmp1->next;
  354. #ifdef DOT_LOCK
  355.         dot_unlock(tmp1->option);
  356. #endif /* DOT_LOCK */
  357.         xfree(tmp1->option);
  358. #ifndef LCKDFLDIR
  359.         /* LCKDFLDIR needs lk_fclose(), so let caller do it */
  360.         n = fclose(fileptr);
  361. #endif /* !LCKDFLDIR */
  362.         xfree(tmp1);
  363.         break;
  364.     }
  365.     return n;
  366. }
  367.  
  368. FILE *
  369. lock_fopen(filename, mode)
  370. char *filename;
  371. char *mode;
  372. {
  373.     FILE *mail_fp = NULL_FILE;
  374.     struct options exclude;
  375.     int fd, lk;
  376.     int cnt = 0;
  377.     SIGRET (*oldint)(), (*oldquit)();
  378. #ifdef LCKDFLDIR
  379.     extern FILE *lk_fopen();
  380. #endif /* !LCKDFLDIR */
  381.  
  382.     if (debug && do_set(set_options, "deadlock")) {
  383.     (void) un_set(&set_options, "deadlock");
  384.     return NULL_FILE;
  385.     }
  386.  
  387.     if (!(mail_fp = exclusive_fopen(filename, mode)))
  388.     return NULL_FILE;
  389.     fd = fileno(mail_fp);
  390.  
  391.     if (mode[0] != 'r' || mode[1] == '+')
  392.     lk = LOCK_EX | LOCK_NB;
  393.     else
  394.     lk = LOCK_SH | LOCK_NB;
  395.  
  396.     on_intr();
  397. #ifdef LCKDFLDIR
  398.     (void) fclose(mail_fp);
  399.     while (isoff(glob_flags, WAS_INTR))
  400.     if (mail_fp = lk_fopen(filename, mode, NULL, NULL, 0)) {
  401.         /* See note in exclusive_fopen() above */
  402.         exclude_list->value = (char *)mail_fp;
  403.         break;
  404.     } else /* uses the open brace below the #endif LCKDFLDIR */
  405. #else /* !LCKDFLDIR */
  406.     while (isoff(glob_flags, WAS_INTR) && flock(fd, lk))
  407. #endif /* LCKDFLDIR */
  408.     {
  409. #ifdef LCKDFLDIR
  410.     if (Access(filename, any(mode, "aw+") ? W_OK : R_OK) == 0)
  411. #else /* !LCKDFLDIR */
  412.     if (errno == EWOULDBLOCK
  413. #ifdef M_UNIX
  414.         || errno == EACCES
  415. #endif /* M_UNIX */
  416.                 )
  417. #endif /* LCKDFLDIR */
  418.     {
  419.         if (isoff(glob_flags, REDIRECT))
  420.         if (!cnt++)
  421.             print("\nwaiting to lock");
  422.         else
  423.             print(".");
  424.     } else {
  425.         error("Unable to lock \"%s\"", filename);
  426.         (void) exclusive_fclose(mail_fp);
  427.         off_intr();
  428.         return NULL_FILE;
  429.     }
  430.     (void) fflush(stdout);
  431.     sleep(1);
  432.     }
  433.     if (cnt)
  434.     print("\n");
  435.     cnt = (ison(glob_flags, WAS_INTR) != 0);
  436.     off_intr();
  437.     if (cnt) {
  438.     (void) exclusive_fclose(mail_fp);
  439.     return NULL_FILE;
  440.     }
  441.     return mail_fp;
  442. }
  443.  
  444. /*ARGSUSED*/
  445. close_lock(filename, fp)
  446. char *filename;
  447. FILE *fp;
  448. {
  449. #ifdef LCKDFLDIR
  450.     (void) exclusive_fclose(fp); /* Only removes the list elem */
  451.     return lk_fclose(fp, filename, NULL, NULL);
  452. #else /* !LCKDFLDIR */
  453.     fflush(fp);
  454.     (void) flock(fileno(fp), LOCK_UN);
  455.     return exclusive_fclose(fp);
  456. #endif /* LCKDFLDIR */
  457. }
  458.  
  459. /* Make sure we don't leave dead lockfiles lying around */
  460. void
  461. droplocks()
  462. {
  463.     while (exclude_list)
  464.     close_lock(exclude_list->option, (FILE *)exclude_list->value);
  465. }
  466.  
  467. #endif /* LOCK_PROG */
  468.