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