source: trunk/athena/etc/track/track.c @ 12350

Revision 12350, 19.1 KB checked in by ghudson, 26 years ago (diff)
Some RCS ID cleanup: delete $Log$ and replace other RCS keywords with $Id$.
Line 
1/*
2 *      $Id: track.c,v 4.21 1999-01-22 23:16:03 ghudson Exp $
3 */
4
5#ifndef lint
6static char *rcsid_header_h = "$Id: track.c,v 4.21 1999-01-22 23:16:03 ghudson Exp $";
7#endif lint
8
9#include "bellcore-copyright.h"
10#include "mit-copyright.h"
11
12#include "track.h"
13#ifdef ultrix
14#include <sys/mount.h>
15#endif
16
17char admin[WORDLEN] = DEF_ADM;          /* track administrator */
18char workdir[LINELEN];                  /* working directory under src/dest
19                                         * root where slists, statfiles, etc.
20                                         * can be found */
21char binarydir[LINELEN] = DEF_BINDIR;   /* directory in working dir to
22                                         * find executables */
23char fromroot[LINELEN] = DEF_FROMROOT;  /* Root directory for source */
24char toroot[LINELEN] = DEF_TOROOT;      /* Root directory for destination */
25
26char lockpath[LINELEN];                 /* starting lock filename */
27char logfilepath[LINELEN] = DEF_LOG;    /* default log file */
28FILE *logfile = NULL;                   /* the logfile */
29char subfilename[LINELEN] = DEF_SUB;    /* default subscription file */
30char subfilepath[LINELEN] = "";         /* alternate subscription file */
31char statfilepath[LINELEN];             /* pathname to statfile */
32FILE *statfile;                         /* the statfile! */
33Statline *statfilebufs;                 /* array of line buffers for sort() */
34int cur_line;                           /* index into statfilebufs      */
35unsigned maxlines = 0;                  /* max # lines in statfile buf array.
36                                         * write_statline maintains maxlines */
37unsigned stackmax = STACKMAX;           /* max depth of pathname-stack vars */
38
39char prgname[LINELEN];
40
41extern int errno;               /* global error number location */
42char errmsg[LINELEN];
43
44int writeflag = 0;      /* if set, translate subscription list -> statfile,
45                         * rather than pulling files */
46int parseflag = 0;      /* if set, just parse the subscription list */
47int forceflag = 0;      /* if set, will over-ride lock files */
48int verboseflag = 0;    /* if set, files listed on stdout as they're updated */
49int cksumflag = 0;      /* if set, compare file checksums when updating */
50int nopullflag = 0;     /* if set, find out the differences,
51                         *         but don't pull anything */
52int quietflag = 0;      /* if set, don't print non-fatal error messages */
53int interactive = 1;    /* if set, don't send errors via mail, print them */
54int uflag = NO_CLOBBER; /* if set, copy a older file on top of a newer one */
55int debug = 0;          /* if set, print debugging information */
56int ignore_prots = 0;   /* if set, don't use or set uid/gid/mode-bits */
57int incl_devs = 0;      /* if set, include devices in update */
58
59/* initialize the global entry-counters with bad values,
60 * so that printmsg() can detect them.
61 */
62Entry entries[ ENTRYMAX];       /* Subscription list entries */
63int entrycnt = -1;              /* Number of entries */
64int entnum = -1;                /* Current entry number */
65
66main(argc,argv)
67int argc;
68char **argv;
69{
70        char    scratch[LINELEN];
71        int     cleanup();
72        int     i;
73
74        strcpy(prgname,argv[0]);
75        strcpy(errmsg,"");
76
77        umask(022);     /* set default umask for daemons */
78
79#ifdef SYSV
80        sigset( SIGINT, (void *)cleanup);
81        sigset( SIGHUP, (void *)cleanup);
82        sigset( SIGPIPE, (void *)cleanup);
83#else
84        signal( SIGINT, cleanup);
85        signal( SIGHUP, cleanup);
86        signal( SIGPIPE, cleanup);
87#endif
88
89        for(i=1;i<argc;i++) {
90                if (argv[i][0] != '-') {
91                        strcpy( subfilepath, argv[i]);
92                        continue;
93                }
94                switch (argv[i][1]) {
95                /* -F dirname
96                 *    Specify source "root" directory.
97                 */
98                case 'F':
99                        get_arg(scratch,argv,&i);
100                        if (*scratch != '/') {
101                                getcwd( fromroot, sizeof(fromroot));
102                                strcat( strcat( fromroot, "/"), scratch);
103                        }
104                        else if (! scratch[1]) *fromroot = '\0';
105                        else strcpy( fromroot, scratch);
106                        break;
107
108                /* -I
109                 *    Ignore protections (uid,gid,mode) when tracking,
110                 *    except when creating a file: then, use remote prots.
111                 */
112                case 'I':
113                        ignore_prots = 1;
114                        break;
115
116                /* -S stackmax
117                 *    Specify deeper path stacks
118                 */
119                case 'S':
120                        get_arg(scratch,argv,&i);
121                        sscanf( scratch, "%d", &stackmax);
122                        break;
123
124                /* -T dirname
125                 *    Specify destination "root" directory.
126                 */
127                case 'T':
128                        get_arg(scratch,argv,&i);
129                        if (*scratch != '/') {
130                                getcwd( toroot, sizeof(toroot));
131                                strcat( strcat( toroot, "/"), scratch);
132                        }
133                        else if (! scratch[1]) *toroot = '\0';
134                        else strcpy( toroot, scratch);
135                        break;
136
137                /* -W dirname
138                 *    Specify the working directory for
139                 *    accessing the subscription-list and statfile.
140                 */
141                case 'W':
142                        get_arg(workdir,argv,&i);
143                        break;
144                /* -c
145                 *    compare checksums of regular files, when updating.
146                 *    the checksums are used to detect file-system
147                 *    corruption.
148                 */
149                case 'c':
150                        cksumflag = 1;
151                        break;
152                /* -d
153                 *    Include devices in an update.
154                 */
155                case 'd':
156                        incl_devs = 1;
157                        break;
158                /* -f
159                 *    Force updating regardless of locks.
160                 */
161                case 'f':
162                        forceflag = 1;
163                        break;
164
165                /* -m {user}
166                 *    Send mail to root/user instead of
167                 * displaying messages on the terminal.
168                 */
169                case 'm':
170                        interactive = 0;
171                        get_arg(admin,argv,&i);
172                        break;
173                /* -n
174                 *    Produce a list of files that need updating,
175                 * but don't actually do anything about them.
176                 */
177                case 'n':
178                        nopullflag = 1;
179                        verboseflag = 1;
180                        fprintf( stderr, "-n: what we WOULD do:\n");
181                        break;
182                /* -p
183                 *    Parse only.  Display a detailed list of the fields in the
184                 * subscription file.
185                 */
186                case 'p':
187                        parseflag = 1;
188                        break;
189                /* -q
190                 *    Be quiet about warning messages.
191                 */
192                case 'q':
193                        quietflag = 1;
194                        break;
195                /* -s {pathname}
196                 *   use specified file as statfile,
197                 * or use stdio if pathname is "-".
198                 */
199                case 's':
200                        get_arg( statfilepath, argv, &i);
201                        break;
202                /* -u
203                 *    Copy over files regardless of which is newer.
204                 */
205                case 'u':
206                        uflag = DO_CLOBBER;
207                        break;
208                /* -v
209                 *    Explain what is going on verbosely.
210                 */
211                case 'v':
212                        verboseflag = 1;
213                        break;
214                /* -w
215                 *    Create a statfile.
216                 */
217                case 'w':
218                        writeflag = 1;
219                        break;
220                /* -x
221                 *    Display debugging information.
222                 */
223                case 'x':
224                        debug = 1;
225                        break;
226                /*
227                 * Something isn't right if we got this far...
228                 */
229                default:
230                        fprintf(stderr,"track error: bad option %s\n",argv[i]);
231                        break;
232                }
233        }
234
235        /*
236         * Set up nullmail interface if not an interactive session.
237         */
238        if (!interactive)
239                setuperr();
240
241        /* check for existence of root directories:
242         * we shouldn't create them, as they are likely to be remote,
243         * so that the user may have forgotten to attach them.
244         */
245        if ( *fromroot && access( fromroot, 0)) {
246                sprintf(errmsg,"can't access source root-directory %s\n",
247                        fromroot);
248                do_panic();
249        }
250        if ( !writeflag && *toroot && access( toroot, 0)) {
251                sprintf(errmsg,"can't access target root-directory %s\n",
252                        toroot);
253                do_panic();
254        }
255        build_path( fromroot, workdir, DEF_SLISTDIR, subfilepath);
256        build_path( fromroot, workdir, DEF_STATDIR, statfilepath);
257
258        fprintf( stderr, "using %s as subscription-list\n", subfilepath);
259        fprintf( stderr, "using %s as statfile\n",         statfilepath);
260
261        /*
262        **      redirect yacc/lex i/o
263        */
264        parseinit( opensubfile( subfilepath));
265        if (yyparse()) {
266                strcpy(errmsg,"parse aborted.\n");
267                do_panic();
268        }
269        if (debug)
270                printf("parse worked\n");
271
272        sort_entries();
273
274        if (parseflag) {  /* -p: Just show the fields */
275                justshow();
276                cleanup();
277        }
278
279        setlock();
280
281        openstat( statfilepath, writeflag);
282
283        if ( writeflag)         /* -w: Write the exporting statfile */
284                writestat();
285        else {
286                /* update in two passes: links & their parent-dirs first,
287                 * which frees up file-system space when links replace files,
288                 * then everything else. re-update dirs in second pass,
289                 * to facilitate statfile-traversal.
290                 */
291                readstat( "ld");
292                rewind( statfile);
293                readstat( "fdbc");
294        }
295        closestat();
296
297        clearlocks();
298        exit( 0);
299}                       /* end of main() */
300
301#define pathtail( p) p[1+*(int*)p[CNT]]
302
303readstat( types) char *types; {
304        struct currentness rem_currency, *cmp_currency;
305        char statline[ LINELEN], *remname;
306        char **from, **to, **cmp;
307        char *tail = NULL;
308
309        from = initpath( fromroot);
310        to   = initpath(   toroot);
311        cmp  = initpath(   toroot);
312
313        /* prime the path-stacks for dec_entry() to
314         * pop the "old" entry-names off.
315         */
316        pushpath( from, ""); pushpath( to, ""); pushpath( cmp, "");
317
318        init_next_match();
319
320        while ( NULL != fgets( statline, sizeof statline, statfile)) {
321
322                /* XXX : needs data-hiding work, but will do:
323                 *       only update what main tells us to in this pass.
324                 */
325                if ( ! strchr( types, *statline)) continue;
326
327                /* extract the currency data from the statline:
328                 */
329                remname = dec_statfile( statline, &rem_currency);
330
331                /* find the subscription entry corresponding to the
332                 * current pathname.
333                 * if we reach an entry which is lexicographically greater than
334                 * the current pathname, read statfile for the next pathname.
335                 * both entries[] & statfile must be sorted by sortkey!
336                 */
337                if ( 0 >= ( entnum = get_next_match( remname))) continue;
338
339                /* do a breadth-first search of the tree of entries,
340                 * to find the entry corresponding to remname:
341                 * for example, if /usr & /usr/bin are both entries,
342                 * they appear in that order in the entries[] array.
343                 * if remname is /usr/bin/foo, we want gettail() to
344                 * use /usr/bin's exception-list, not /usr's exception-list.
345                 * thus, /usr/bin is the "last match" for /usr/bin/foo.
346                 */
347                entnum = last_match( remname, entnum);
348
349                tail = remname;
350                switch ( gettail( &tail, TYPE( rem_currency.sbuf), entnum)) {
351                case NORMALCASE: break;
352                case DONT_TRACK: continue;
353                case FORCE_LINK: fake_link( fromroot, remname, &rem_currency);
354                                 break;
355                default:         sprintf(errmsg,"bad value from gettail\n");
356                                 do_panic();
357                }
358
359                /* loosely, tail == remname - fromfile, as
360                 * long as tail isn't in the exception-list.
361                 * the string remname begins with the string from[ PATH]:
362                 * for example: remname =            /usr/bin/foo.
363                 *          from[ PATH] =            /usr/bin.
364                 *          from[ ROOT] = /mountpoint/usr/bin.
365                 * in this example, we get tail == "foo".
366                 */
367
368                cmp_currency = dec_entry( entnum, from, to, cmp, tail);
369
370                pushpath( to,  tail);
371                pushpath( from, tail);
372
373                if ( ! update_file( cmp_currency, to,
374                                   &rem_currency, from))
375                        /* REWRITE:
376                        do_cmds( entries[entnum].cmdbuf, to[ ROOT])
377                         */
378                        ;
379                /* remove tail from each path:
380                 */
381                poppath( to);
382                poppath( from);
383        }
384        /* track is often used just before a reboot;
385         * flush the kernel's text-table,
386         * to ensure that the vnodes we've freed get scavenged,
387         */
388#ifdef ultrix
389        {
390                dev_t dev;
391                struct fs_data fsd;
392                if(statfs("/",&fsd) == 1) umount(fsd.fd_dev);
393        }
394#endif
395        /* then make sure that the file-systems' superblocks are up-to-date.
396         */
397        sync();
398        sleep(2);
399}
400
401/*
402 * Set lock for subscriptionlist file.
403 */
404
405setlock()
406{
407        sprintf( lockpath,"%s/%s.started", DEF_LOCKDIR, subfilename);
408
409        if ( access( lockpath, 0));
410        else if ( too_old( lockpath, LOCK_TIME) || forceflag) clearlocks();
411        else {
412                sprintf( errmsg, "lock set on %s--quitting\n", lockpath);
413                do_gripe();
414                exit(0);
415        }
416        if ( close( creat( lockpath,220))) {
417                sprintf( errmsg, "can't create lockfile %s\n", lockpath);
418                do_panic();
419        }
420        return(0);
421}
422
423/*
424 * Erase those locks...
425 */
426
427clearlocks()
428{
429        if ( !*lockpath) return;
430        if ( unlink( lockpath)) {
431                fprintf( stderr, "can't remove lockfile %s", lockpath);
432                perror( "system error is: ");
433        }
434        else if ( verboseflag)
435                fprintf( stderr, "cleared lock %s\n",lockpath);
436}
437
438/* the array from[] is a set of pointers into a single pathname.
439 * it allows us to pass a parsed pathname along in walk_trees()'
440 * recursive descent of a directory.
441 * the ROOT component is the entire absolute pathname,
442 * including the mount-point, for use in file-system calls.
443 * the NAME component is the portable pathname, without the mount-point,
444 * as the file is described in the statfile.
445 * the TAIL component is everything that's added during the recursive descent,
446 * for comparison with the exception-list. TAIL lacks both mount-point &
447 * the fromfile name.
448 */
449
450/*
451 * Act like a librarian and write out the statfile.
452 */
453
454writestat()
455{
456        char **from, **cmp, **dummy = (char **) NULL;
457        struct currentness *entry_currency;
458
459        from = initpath( fromroot);
460        cmp  = initpath( fromroot);
461
462        /* prime the path-stacks for dec_entry() to
463         * pop the "old" entry-names off.
464         */
465        pushpath( from, ""); pushpath( cmp, "");
466
467        for( entnum = 1; entnum < entrycnt; entnum++) {
468
469                /* dec_entry pushes pathname-qualification
470                 * onto the paths 'from' & 'cmp',
471                 * and pops when appropriate.
472                 */
473                entry_currency = dec_entry( entnum, from, dummy, cmp, NULL);
474
475                if (entries[entnum].islink) {
476                        fake_link( "", from[ NAME], entry_currency);
477                        write_statline( from, entry_currency);
478                        continue;
479                }
480
481                /* write_statline returns fromfile's true type,
482                 * regardless of cmpfile's type:
483                 */
484                if      ( S_IFDIR != write_statline( from, entry_currency));
485                else if ( S_IFDIR != TYPE( entry_currency->sbuf))
486
487                        walk_trees( from, dummy, entry_currency);
488                else    walk_trees( from, cmp,   entry_currency);
489
490                /* WARNING: walk_trees alters ALL of its arguments */
491                /* sort the statfile, and write it out
492                 * to the correct directory:
493                 */
494        }
495        sort_stat();
496}
497
498/* if the current entry's fromfile is a directory, but its cmpfile isn't,
499 * put the same cmpstat in all of fromfile's contents' statlines.
500 * if cmpfile is a directory too, its subtree must parallel fromfile's subtree,
501 * and each statline reflects the one-to-one (not onto) mapping from
502 * fromfile's subtree to cmpfile's subtree:
503 * we take fromfile's subnode's pathname ( not including the prefix fromroot),
504 * and we take the stat from cmpfile's corresponding subnode.
505 */
506walk_trees( f, c, currency)
507char *f[], *c[];
508struct currentness *currency;
509{
510        DIR *dirp;
511#ifdef POSIX
512        struct dirent *dp;
513#else
514        struct direct *dp;
515#endif
516        char *tail;
517
518        dirp = opendir( f[ ROOT]);
519        if (!dirp) {
520                sprintf(errmsg,"can't open directory %s\n", f[ ROOT]);
521                do_gripe();
522                return;
523        }
524
525        while( dp = readdir( dirp)) {
526                if (strcmp(dp->d_name, ".") == 0)
527                  continue;
528                if (strcmp(dp->d_name, "..") == 0)
529                  continue;
530
531                if (! dp->d_ino) continue;    /* empty dir-block */
532
533                pushpath( f, dp->d_name);
534                pushpath( c, dp->d_name);
535
536                tail = f[ NAME];
537                switch ( gettail( &tail, 0, entnum)) {
538                case NORMALCASE: break;
539                case FORCE_LINK: fake_link( "", f[ NAME], currency);
540                                 write_statline( f, currency);
541                                 /* fall through to poppath() calls */
542                case DONT_TRACK: poppath( f);
543                                 poppath( c);
544                                 continue;
545                default:         sprintf(errmsg,"bad value from gettail\n");
546                                 do_panic();
547                }
548                /* normal case: tail isn't an exception or a forced link.
549                 */
550                if ( c && get_currentness( c, currency)) {
551                        sprintf(errmsg,"can't lstat comparison-file %s.\n",
552                                c[ ROOT]);
553                        do_panic();
554                }
555                /* write_statline returns fromfile's type:
556                 */
557                else if ( S_IFDIR == write_statline( f, currency))
558                        walk_trees( f, c, currency);
559
560                poppath( f);
561                poppath( c);
562        }
563        closedir(dirp);
564}
565
566/*
567 * Get a command line argument
568 */
569
570get_arg(to,list,ptr)
571char *to,**list;
572int *ptr;
573{
574        int offset = 2;
575
576        if (strlen(list[*ptr]) == 2) {
577                (*ptr)++;
578                offset = 0;
579        }
580        strcpy(to,list[*ptr]+offset);
581}
582
583/*
584 * Log a message to the logfile.
585   UNUSED
586
587log(ptr)
588char *ptr;
589{
590        extern long time();
591        extern char *ctime();
592        static FILE *logfile = NULL;
593        char namebuf[LINELEN];
594        char timestring[LINELEN];
595        long timebuf;
596
597        if ( NULL == logfile) {
598                sprintf( namebuf,"%s/%s",workdir,DEF_LOG);
599                if( access( namebuf, 0))
600                        return;
601                if (NULL == (logfile = fopen( namebuf,"a"))) {
602                        sprintf(errmsg,"can't open log file %s",namebuf);
603                        do_panic();
604                }
605        }
606        timebuf = time(0);
607        strcpy(timestring,ctime(&timebuf));
608        timestring[ strlen(timestring)-1] = '\0';
609        fprintf(logfile,"%s %s %s\n",timestring,subfilename,ptr);
610}
611 */
612
613#undef ROOT
614
615/*
616 * Execute shell commands
617 */
618
619do_cmds(cmds,local)
620char *cmds,*local;
621{
622        char *ptr,*nptr;
623        FILE *shell;
624
625        if ( ! *cmds) return;
626        ptr = cmds;
627
628        shell = popen(DEF_SHELL,"w");
629        if (!shell) {
630                sprintf(errmsg,"can't open shell %s\n",DEF_SHELL);
631                do_gripe();
632                return;
633        }
634
635        fprintf(shell,"chdir %s\n",toroot);
636        fprintf(shell,"%sFILE=%s\n",DEF_SETCMD,local);
637        fprintf(shell,"%sROOT=%s\n",DEF_SETCMD,toroot);
638
639        for (;;) {
640                nptr = strchr(ptr,'\n');
641                if (nptr)
642                        *nptr = '\0';
643                fprintf(shell,"%s\n",ptr);
644                if (!nptr) {
645                        pclose(shell);
646                        return;
647                }
648                ptr = nptr+1;
649        }
650}
651/*
652 * Show parsing.
653 */
654
655justshow()
656{
657        int i,j, size;
658        Entry *e;
659        List_element *p;
660
661        fprintf( stderr, "subscription-list as parsed:\n\n");
662
663        for (i = 0; i <= entrycnt; i++) {
664                e = &entries[ i];
665                if ( ! e->fromfile) break;
666                fprintf(stderr,
667                        "entry %d:\n\tislink-- %d\tfromfile-- %s\n",
668                        i,
669                        e->islink,
670                        e->fromfile);
671                fprintf(stderr,
672                        "\tcmpfile-- %s\n\ttofile-- %s\n\tpatterns--\n",
673                        e->cmpfile,
674                        e->tofile);
675                for( p = e->patterns; p ; p = NEXT( p))
676                    fprintf(stderr,"\t\t%s%s\n",
677                            FLAG( p) == FORCE_LINK ? "-> " : "",
678                            TEXT( p));
679                fprintf( stderr, "\texceptions--\n");
680                switch( SIGN( e->names.shift)) {
681                case -1:
682                    for( p = ( List_element *) e->names.table; p ; p = NEXT( p))
683                        fprintf(stderr,"\t\t%s%s\n",
684                                FLAG( p) == FORCE_LINK ? "-> " : "",
685                                TEXT( p));
686                    fprintf(stderr,
687                        "track didn't fully parse the exception-list.\n");
688                    fprintf(stderr,
689                        "the most-recently parsed exception was:\n%s%s\n",
690                        FLAG( e->names.table) == FORCE_LINK ? "-> " : "",
691                        TEXT( e->names.table));
692                    continue;
693                case 0: break;
694                case 1:
695                    size = (unsigned) 0x80000000 >> e->names.shift - 1;
696                    for( j = 0; j < size; j++)
697                    {
698                        if ( ! e->names.table[j]) continue;
699                        fprintf( stderr,"\t\t");
700                        for ( p = e->names.table[j]; p; p = NEXT( p))
701                            fprintf(stderr,"%s%s, ",
702                                    FLAG( p) == FORCE_LINK ? "-> " : "",
703                                    TEXT( p));
704                        fprintf( stderr,"\n");
705                    }
706                    break;
707                }
708                fprintf(stderr,"\tcommand-- %s\n",e->cmdbuf);
709        }
710}
711
712/*
713 *      redirect standard error output to mail to the administrator
714 *      use nullmail so that null messages won't get sent.
715 */
716
717/* REWORK */
718
719setuperr()
720{
721        char msg[LINELEN];
722        FILE *tmp;
723
724        sprintf(msg,"%s/nullmail %s",binarydir,admin);
725        /*
726        **      start a process that will send mail to the adminstrator
727        */
728        if ((tmp = popen(msg,"w")) == NULL) {
729/*              sprintf( msg, "echo HELP track --%s %s", gargv[0],
730                        "can't execute nullmail cmd  > /dev/console");
731                system(msg); */
732                exit(1);
733        }
734        /*
735        **      now connect stderr to the pipe
736        */
737        if (dup2(fileno(tmp),2) ==  -1) {
738/*              sprintf( msg, "echo HELP track --%s %s", gargv[0],
739                        "can't dup stderr  > /dev/console");
740                system(msg); */
741                exit(1);
742        }
743}
744
745build_path( f, w, d, p) char *f, *w, *d, *p; {
746        static char buf[ LINELEN];
747
748        if ( ! strcmp( p, "-")) return;
749        /*
750         * Get the proper working directory,
751         * where the subscription-list & statfile are.
752         */
753        if ( *w) f = "";  /* don't add root-qualification to user's workdir */
754        else if ( *p) f = "."; /* don't use default workdir with user's filen */
755        else w = DEF_WORKDIR; /* default workdir, default filename */
756
757        if ( *p) d = "";
758        else strcpy( p, subfilename);
759
760        sprintf( buf, "%s%s%s%s/%s", f, w, *d ? "/" : "", d, p);
761        strcpy( p, buf);
762}
763
764FILE *
765opensubfile( path) char *path; {
766        FILE *subfile;
767        if ( ! ( subfile = fopen( path, "r"))) {
768                sprintf( errmsg, "Can't open subscriptionlist %s\n", path);
769                do_panic();
770        }
771        return( subfile);
772}
773
774openstat( path, write) char *path; int write;
775{
776        char *mode = write? "w"   : "r";
777        FILE *std =  write? stdout : stdin;
778
779        if ( ! strcmp( path, "-"))
780                statfile = std;
781        else if ( ! ( statfile = fopen( path, mode))) {
782                sprintf( errmsg, "can't open statfile %s\n", path);
783                do_panic();
784        }
785}
786
787closestat() {
788        if ( EOF == fclose( statfile)) {
789                sprintf( errmsg, "can't close %s\n", statfilepath);
790                do_panic();
791        }
792}
793
794cleanup()
795{
796        clearlocks();
797        exit(0);
798}
Note: See TracBrowser for help on using the repository browser.