/* ** ATOP - System & Process Monitor ** ** The program 'atop' offers the possibility to view the activity of ** the system on system-level as well as process-level. ** ** This source-file contains functions to manipulate with process-accounting ** features (switch it on/off and read the process-accounting records). ** ================================================================ ** Author: Gerlof Langeveld ** E-mail: gerlof.langeveld@atoptool.nl ** Date: November 1996 ** LINUX-port: June 2000 ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2, or (at your option) any ** later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU General Public License for more details. ** ** $Log: acctproc.c,v $ ** Revision 1.28 2010/04/23 12:20:19 gerlof ** Modified mail-address in header. ** ** Revision 1.27 2009/12/12 10:12:01 gerlof ** Register and display end date and end time for process. ** ** Revision 1.26 2008/03/06 08:37:25 gerlof ** Register/show ppid of a process. ** ** Revision 1.25 2008/01/14 09:22:23 gerlof ** Support for environment variable ATOPACCT to specify the name of a ** particular process accouting file. ** ** Revision 1.24 2007/08/17 08:50:26 gerlof ** Verify if private accounting used before switching off accounting. ** ** Revision 1.23 2007/03/20 13:01:51 gerlof ** Introduction of variable supportflags. ** ** Revision 1.22 2007/02/13 09:14:49 gerlof ** New boolean introduced to indicate if accounting is active. ** ** Revision 1.21 2006/02/07 06:46:15 gerlof ** Removed swap-counter. ** ** Revision 1.20 2005/11/04 14:17:10 gerlof ** Improved recognition of certain version process accounting file. ** ** Revision 1.19 2005/10/31 12:44:58 gerlof ** Support account-record version 3 (used by Mandriva). ** ** Revision 1.18 2005/10/31 08:55:05 root ** *** empty log message *** ** ** Revision 1.17 2004/12/14 15:05:00 gerlof ** Implementation of patch-recognition for disk and network-statistics. ** ** Revision 1.16 2004/06/01 11:57:22 gerlof ** Consider other standard accounting-files, i.e. /var/account/pacct. ** ** Revision 1.15 2004/05/06 09:48:21 gerlof ** Ported to kernel-version 2.6. ** ** Revision 1.14 2003/07/07 09:26:15 gerlof ** Cleanup code (-Wall proof). ** ** Revision 1.13 2003/07/02 06:43:11 gerlof ** Modified include-file sys/acct.h to linux/acct.h to make it ** work on Alpha-based systems as well. ** ** Revision 1.12 2003/06/27 12:31:24 gerlof ** Adapt long to long long. ** ** Revision 1.11 2003/04/03 08:32:58 gerlof ** Cosmetic changes. ** ** Revision 1.10 2003/01/14 09:01:45 gerlof ** Small cosmetic changes. ** ** Revision 1.9 2002/10/03 11:12:03 gerlof ** Modify (effective) uid/gid to real uid/gid. ** ** Revision 1.8 2002/07/24 11:11:12 gerlof ** Redesigned to ease porting to other UNIX-platforms. ** ** Revision 1.7 2002/07/11 07:28:29 root ** Some additions related to secure accounting file handling ** ** Revision 1.6 2002/07/08 09:14:54 root ** Modified secure handling of accounting file ** (inspired by Tobias Rittweiler). ** ** Revision 1.5 2001/11/07 09:16:55 gerlof ** Allow users to run atop without process-accounting switched on. ** ** Revision 1.4 2001/10/16 06:15:52 gerlof ** Partly redesigned. ** ** Revision 1.3 2001/10/04 13:02:24 gerlof ** Redesign of the way to determine how many atop's are using process-accounting ** on basis of semaphores. ** ** Revision 1.2 2001/10/04 08:46:46 gerlof ** Improved verification of kernel-symbol addresses ** ** Revision 1.1 2001/10/02 10:38:35 gerlof ** Initial revision ** */ static const char rcsid[] = "$Id: acctproc.c,v 1.28 2010/04/23 12:20:19 gerlof Exp $"; #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include "atop.h" #include "photoproc.h" #include "acctproc.h" #include "atopacctd.h" #define ACCTDIR "/tmp/atop.d" #define ACCTFILE "atop.acct" #define ACCTENV "ATOPACCT" static char acctatop; /* boolean: atop's own accounting busy ? */ static off_t acctsize; /* previous size of account file */ static int acctrecsz; /* size of account record */ static int acctversion; /* version of account record layout */ static int acctfd = -1; /* fd of account file (-1 = not open) */ static long curshadowseq; /* current shadow file sequence number */ static long maxshadowrec; /* number of records per shadow file */ static char *pacctdir = PACCTDIR; static count_t acctexp (comp_t ct); static int acctvers(int); static void acctrestarttrial(); static void switchshadow(void); struct pacctadm { char *name; struct stat stat; } pacctadm[] = { { "/var/log/pacct", {0, }, }, { "/var/account/pacct", {0, }, } }; /* ** Semaphore-handling ** ** A semaphore-group with two semaphores is created ** ** 0 - Binary semaphore (mutex) to get access to active atop-counter ** ** 1 - Active atop-counter (inverted). ** This semaphore is initialized at some high value and is ** decremented by every atop-incarnation which uses the private ** accounting-file and incremented as soon as such atop stops again. ** If an atop-incarnation stops and it appears to be the last one ** using the private accounting-file, accounting is stopped ** and the file removed ** (Yes, I know: it's not proper usage of the semaphore-principle). */ #define ATOPACCTKEY 3121959 #define ATOPACCTTOT 100 struct sembuf semclaim = {0, -1, SEM_UNDO}, semrelse = {0, +1, SEM_UNDO}, semdecre = {1, -1, SEM_UNDO}, semincre = {1, +1, SEM_UNDO}; /* ** switch on the process-accounting mechanism ** ** return value: ** 0 - activated (success) ** 1 - not activated: unreadable accounting file ** 2 - not activated: empty environment variable ATOPACCT ** 3 - not activated: no access to semaphore group ** 4 - not activated: impossible to create own accounting file ** 5 - not activated: no root privileges */ int acctswon(void) { int i, j, sematopid, sempacctpubid; static ushort vals[] = {1, ATOPACCTTOT}; union {ushort *array;} arg = {vals}; struct stat statbuf; char *ep; /* ** when a particular environment variable is present, atop should ** use a specific accounting-file (as defined by the environment ** variable) or should use no process accounting at all (when ** contents of environment variable is empty) */ if ( (ep = getenv(ACCTENV)) ) { /* ** environment variable exists */ if (*ep) { /* ** open active account file with the specified name */ if (! droprootprivs() ) cleanstop(42); if ( (acctfd = open(ep, O_RDONLY) ) == -1) return 1; if ( !acctvers(acctfd) ) { (void) close(acctfd); acctfd = -1; return 1; } supportflags |= ACCTACTIVE; return 0; } else { /* ** no contents */ return 2; } } /* ** when the atopacctd daemon is active on this system, ** it should be the preferred way to read the accounting records */ if ( (sempacctpubid = semget(PACCTPUBKEY, 0, 0)) != -1) { FILE *cfp; char shadowpath[128]; struct flock flock; if (! droprootprivs() ) cleanstop(42); (void) semop(sempacctpubid, &semclaim, 1); snprintf(shadowpath, sizeof shadowpath, "%s/%s/%s", pacctdir, PACCTSHADOWD, PACCTSHADOWC); if ( (cfp = fopen(shadowpath, "r")) ) { if (fscanf(cfp, "%ld/%lu", &curshadowseq, &maxshadowrec) == 2) { fclose(cfp); snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF, pacctdir, PACCTSHADOWD, curshadowseq); if ( (acctfd = open(shadowpath, O_RDONLY))!=-1) { if ( !acctvers(acctfd) ) { int maxcnt = 40; if ( fork() == 0 ) exit(0); (void) wait((int *) 0); while ( !acctvers(acctfd) && --maxcnt) usleep(50000); if (!acctversion) { (void) close(acctfd); acctfd = -1; semop(sempacctpubid, &semrelse, 1); regainrootprivs(); return 1; } } /* ** set read lock on current shadow file */ flock.l_type = F_RDLCK; flock.l_whence = SEEK_SET; flock.l_start = 0; flock.l_len = 1; if ( fcntl(acctfd, F_SETLK, &flock) != -1) { supportflags |= ACCTACTIVE; regainrootprivs(); return 0; } (void) close(acctfd); } } else { fclose(cfp); maxshadowrec = 0; } } (void) semop(sempacctpubid, &semrelse, 1); } /* ** check if process accounting is already switched on ** for one of the standard accounting-files; in that case we ** open this file and get our info from here .... */ for (i=0, j=0; i < sizeof pacctadm/sizeof pacctadm[0]; i++) { if ( stat(pacctadm[i].name, &pacctadm[i].stat) == 0) j++; } if (j) { /* ** at least one file is present; check if it is really in use ** at this moment for accounting by forking a child-process ** which immediately finishes to force a new accounting-record */ if ( fork() == 0 ) exit(0); /* let the child finish */ (void) wait((int *) 0); /* let the parent wait */ for (i=0; i < sizeof pacctadm/sizeof pacctadm[0]; i++) { if ( stat(pacctadm[i].name, &statbuf) == 0) { /* ** accounting-file has grown? */ if (statbuf.st_size > pacctadm[i].stat.st_size) { /* ** open active account file */ if ( (acctfd = open(pacctadm[i].name, O_RDONLY) ) == -1) return 1; if ( !acctvers(acctfd) ) { (void) close(acctfd); acctfd = -1; return 1; } supportflags |= ACCTACTIVE; return 0; } } } } /* ** process-accounting is not yet switched on in a standard way; ** check if another atop has switched on accounting already ** ** first try to create a semaphore group exclusively; if this succeeds, ** this is the first atop-incarnation since boot and the semaphore group ** should be initialized` */ if ( (sematopid = semget(ATOPACCTKEY, 2, 0600|IPC_CREAT|IPC_EXCL)) >= 0) (void) semctl(sematopid, 0, SETALL, arg); else sematopid = semget(ATOPACCTKEY, 0, 0); /* ** check if we got access to the semaphore group */ if (sematopid == -1) return 3; /* ** the semaphore group is opened now; claim exclusive rights */ (void) semop(sematopid, &semclaim, 1); /* ** are we the first to use the accounting-mechanism ? */ if (semctl(sematopid, 1, GETVAL, 0) == ATOPACCTTOT) { /* ** create a new separate directory below /tmp ** for the accounting file; ** if this directory exists (e.g. previous atop-run killed) ** it will be cleaned and newly created */ if ( mkdir(ACCTDIR, 0700) == -1) { if (errno == EEXIST) { (void) acct(0); (void) unlink(ACCTDIR "/" ACCTFILE); (void) rmdir(ACCTDIR); } if ( mkdir(ACCTDIR, 0700) == -1) { /* ** persistent failure */ (void) semop(sematopid, &semrelse, 1); return 4; } } /* ** create accounting file in new directory */ (void) close( creat(ACCTDIR "/" ACCTFILE, 0600) ); /* ** switch on accounting */ if ( acct(ACCTDIR "/" ACCTFILE) < 0) { (void) unlink(ACCTDIR "/" ACCTFILE); (void) rmdir(ACCTDIR); (void) semop(sematopid, &semrelse, 1); return 5; } } /* ** accounting is switched on now; ** open accounting-file */ if ( (acctfd = open(ACCTDIR "/" ACCTFILE, O_RDONLY) ) < 0) { (void) acct(0); (void) unlink(ACCTDIR "/" ACCTFILE); (void) rmdir(ACCTDIR); (void) semop(sematopid, &semrelse, 1); return 1; } /* ** accounting successfully switched on */ (void) semop(sematopid, &semdecre, 1); (void) semop(sematopid, &semrelse, 1); acctatop = 1; /* ** determine version of accounting-record */ (void) fstat(acctfd, &statbuf); if (statbuf.st_size == 0) /* no acct record written yet */ { /* ** let's write one record */ if ( fork() == 0 ) exit(0); /* let the child finish */ (void) wait((int *) 0); /* let the parent wait */ } if ( !acctvers(acctfd) ) { (void) acct(0); (void) close(acctfd); (void) unlink(ACCTDIR "/" ACCTFILE); (void) rmdir(ACCTDIR); acctfd = -1; return 1; } supportflags |= ACCTACTIVE; return 0; } /* ** determine the version of the accounting-record layout/length ** and reposition the seek-pointer to the end of the accounting file */ static int acctvers(int fd) { struct acct tmprec; /* ** read first record from accounting file to verify ** the second byte (always contains version number) */ if ( read(fd, &tmprec, sizeof tmprec) < sizeof tmprec) return 0; switch (tmprec.ac_version & 0x0f) { case 2: acctrecsz = sizeof(struct acct); acctversion = 2; break; case 3: acctrecsz = sizeof(struct acct_v3); acctversion = 3; break; default: fprintf(stderr, "Unknown format of process accounting file\n"); cleanstop(8); } /* ** accounting successfully switched on */ acctsize = acctprocnt() * acctrecsz; /* ** reposition to actual file-size */ (void) lseek(fd, acctsize, SEEK_SET); return 1; } /* ** switch off the process-accounting mechanism */ void acctswoff(void) { int sematopid; struct stat before, after; /* ** if accounting not supported, skip call */ if (acctfd == -1) return; /* ** our private accounting-file in use? */ if (acctatop) { acctatop = 0; /* ** claim the semaphore to get exclusive rights for ** the accounting-manipulation */ sematopid = semget(ATOPACCTKEY, 0, 0); (void) semop(sematopid, &semclaim, 1); (void) semop(sematopid, &semincre, 1); /* ** check if we were the last user of accounting */ if (semctl(sematopid, 1, GETVAL, 0) == ATOPACCTTOT) { /* ** switch off private accounting ** ** verify if private accounting is still in use to ** avoid that we switch off process accounting that ** has been activated manually in the meantime */ (void) fstat(acctfd, &before); if ( fork() == 0 ) /* create a child and */ exit(0); /* let the child finish */ (void) wait((int *) 0); /* let the parent wait */ (void) fstat(acctfd, &after); if (after.st_size > before.st_size) { /* ** remove the directory and file */ regainrootprivs(); /* get root privs again */ (void) acct(0); (void) unlink(ACCTDIR "/" ACCTFILE); (void) rmdir(ACCTDIR); if (! droprootprivs() ) cleanstop(42); } } (void) semop(sematopid, &semrelse, 1); } /* ** anyhow close the accounting-file again */ (void) close(acctfd); /* close account file again */ acctfd = -1; supportflags &= ~ACCTACTIVE; } /* ** get the number of exited processes written ** in the process-account file since the previous sample */ unsigned long acctprocnt(void) { struct stat statacc; /* ** if accounting not supported, skip call */ if (acctfd == -1) return 0; /* ** determine the current size of the accounting file */ (void) fstat(acctfd, &statacc); /* ** handle atopacctd-based process accounting on bases of ** fixed-chunk shadow files */ if (maxshadowrec) { unsigned long numrecs = 0; long newseq; char shadowpath[128]; FILE *cfp; /* ** verify how many new processes are added to the current ** shadow file */ numrecs = (statacc.st_size - acctsize) / acctrecsz; /* ** verify if subsequent shadow files are involved ** (i.e. if the current file is full) */ if (statacc.st_size / acctrecsz < maxshadowrec) return numrecs; /* ** more shadow files available ** get current shadow file */ snprintf(shadowpath, sizeof shadowpath, "%s/%s/%s", pacctdir, PACCTSHADOWD, PACCTSHADOWC); if ( (cfp = fopen(shadowpath, "r")) ) { if (fscanf(cfp, "%ld", &newseq) == 1) { fclose(cfp); } else { fclose(cfp); return numrecs; } } else { return numrecs; } if (newseq == curshadowseq) return numrecs; snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF, pacctdir, PACCTSHADOWD, newseq); /* ** determine the size of newest shadow file */ (void) stat(shadowpath, &statacc); numrecs += ((newseq - curshadowseq - 1) * maxshadowrec) + (statacc.st_size / acctrecsz); return numrecs; } else /* ** classic process accounting on bases of directly opened ** process accounting file */ { if (acctsize > statacc.st_size) /* accounting reset? */ { /* ** reposition to start of file */ (void) lseek(acctfd, 0, SEEK_SET); acctsize = 0; } return (statacc.st_size - acctsize) / acctrecsz; } } /* ** reposition the seek offset in the process accounting file to skip ** processes that have not been read */ void acctrepos(unsigned int noverflow) { /* ** if accounting not supported, skip call */ if (acctfd == -1) return; if (maxshadowrec) { int i; off_t virtoffset = acctsize + noverflow * acctrecsz; off_t maxshadowsz = maxshadowrec * acctrecsz; long switches = virtoffset / maxshadowsz; acctsize = virtoffset % maxshadowsz; for (i=0; i < switches; i++) switchshadow(); (void) lseek(acctfd, acctsize, SEEK_SET); } else { /* ** just reposition to skip superfluous records */ (void) lseek(acctfd, noverflow * acctrecsz, SEEK_CUR); acctsize += noverflow * acctrecsz; } } /* ** read the process records from the process accounting file, ** that are written since the previous cycle */ int acctphotoproc(struct tstat *accproc, int nrprocs) { register int nrexit; register struct tstat *api; struct acct acctrec; struct acct_v3 acctrec_v3; struct stat statacc; /* ** if accounting not supported, skip call */ if (acctfd == -1) return 0; /* ** determine the size of the (current) account file ** and the current offset within that file */ (void) fstat(acctfd, &statacc); /* ** check all exited processes in accounting file */ for (nrexit=0, api=accproc; nrexit < nrprocs; nrexit++, api++, acctsize += acctrecsz) { /* ** in case of shadow accounting files, we might have to ** switch from the current accouting file to the next */ if (maxshadowrec && acctsize >= statacc.st_size) { switchshadow(); /* ** determine the size of the new (current) account file ** and initialize the current offset within that file */ (void) fstat(acctfd, &statacc); acctsize = 0; } /* ** read the next record */ switch (acctversion) { case 2: if ( read(acctfd, &acctrec, acctrecsz) < acctrecsz ) break; /* unexpected end of account file */ /* ** fill process info from accounting-record */ api->gen.state = 'E'; api->gen.nthr = 1; api->gen.isproc = 1; api->gen.pid = 0; api->gen.tgid = 0; api->gen.ppid = 0; api->gen.excode = acctrec.ac_exitcode; api->gen.ruid = acctrec.ac_uid16; api->gen.rgid = acctrec.ac_gid16; api->gen.btime = acctrec.ac_btime; api->gen.elaps = acctrec.ac_etime; api->cpu.stime = acctexp(acctrec.ac_stime); api->cpu.utime = acctexp(acctrec.ac_utime); api->mem.minflt = acctexp(acctrec.ac_minflt); api->mem.majflt = acctexp(acctrec.ac_majflt); api->dsk.rio = acctexp(acctrec.ac_rw); strcpy(api->gen.name, acctrec.ac_comm); break; case 3: if ( read(acctfd, &acctrec_v3, acctrecsz) < acctrecsz ) break; /* unexpected end of account file */ /* ** fill process info from accounting-record */ api->gen.state = 'E'; api->gen.pid = acctrec_v3.ac_pid; api->gen.tgid = acctrec_v3.ac_pid; api->gen.ppid = acctrec_v3.ac_ppid; api->gen.nthr = 1; api->gen.isproc = 1; api->gen.excode = acctrec_v3.ac_exitcode; api->gen.ruid = acctrec_v3.ac_uid; api->gen.rgid = acctrec_v3.ac_gid; api->gen.btime = acctrec_v3.ac_btime; api->gen.elaps = acctrec_v3.ac_etime; api->cpu.stime = acctexp(acctrec_v3.ac_stime); api->cpu.utime = acctexp(acctrec_v3.ac_utime); api->mem.minflt = acctexp(acctrec_v3.ac_minflt); api->mem.majflt = acctexp(acctrec_v3.ac_majflt); api->dsk.rio = acctexp(acctrec_v3.ac_rw); strcpy(api->gen.name, acctrec_v3.ac_comm); break; } } if (acctsize > ACCTMAXFILESZ && !maxshadowrec) acctrestarttrial(); return nrexit; } /* ** when the size of the private accounting file exceeds a certain limit, ** it might be useful to stop process accounting, truncate the ** process accounting file to zero and start process accounting again ** ** this will only be done if this atop process is the only one ** that is currently using the accounting file */ static void acctrestarttrial() { struct stat statacc; int sematopid; /* ** not private accounting-file in use? */ if (!acctatop) return; // do not restart /* ** still remaining records in accounting file that are ** written between the moment that the number of exited ** processes was counted and the moment that all processes ** were read */ (void) fstat(acctfd, &statacc); if (acctsize != statacc.st_size) return; // do not restart /* ** claim the semaphore to get exclusive rights for ** the accounting-manipulation */ sematopid = semget(ATOPACCTKEY, 0, 0); (void) semop(sematopid, &semclaim, 1); /* ** check if there are more users of accounting file */ if (semctl(sematopid, 1, GETVAL, 0) < ATOPACCTTOT-1) { (void) semop(sematopid, &semrelse, 1); return; // do not restart } /* ** restart is possible ** ** - switch off accounting ** - truncate the file ** - switch on accounting */ regainrootprivs(); // get root privs again (void) acct(0); // switch off accounting if ( truncate(ACCTDIR "/" ACCTFILE, 0) == 0) (void) lseek(acctfd, 0, SEEK_SET); (void) acct(ACCTDIR "/" ACCTFILE); if (! droprootprivs() ) cleanstop(42); acctsize = 0; (void) semop(sematopid, &semrelse, 1); } /* ** expand the special compression-methods */ static count_t acctexp(comp_t ct) { count_t exp; count_t val; exp = (ct >> 13) & 0x7; /* obtain 3 bits base-8 exponent */ val = ct & 0x1fff; /* obtain 13 bits mantissa */ while (exp-- > 0) val <<= 3; return val; } /* ** switch to the next accounting shadow file */ static void switchshadow(void) { int tmpfd; char shadowpath[128]; struct flock flock; /* ** determine path name of new shadow file */ curshadowseq++; snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF, pacctdir, PACCTSHADOWD, curshadowseq); /* ** open new shadow file, while the previous is also kept open ** (to keep the read lock until a read lock is set for the new ** shadow file) */ if ( (tmpfd = open(shadowpath, O_RDONLY)) != -1) { /* ** set read lock on new shadow file */ flock.l_type = F_RDLCK; flock.l_whence = SEEK_SET; flock.l_start = 0; flock.l_len = 1; if ( fcntl(tmpfd, F_SETLK, &flock) != -1) { (void) close(acctfd); // implicit release read lock acctfd = tmpfd; return; } else { (void) close(tmpfd); } } } /* ** handle the option 'pacctdir' in the /etc/atoprc file */ void do_pacctdir(char *tagname, char *tagvalue) { char shadowpath[128]; struct stat dirstat; /* ** copy the directory pathname to an own buffer */ if ( (pacctdir = malloc(strlen(tagvalue)+1)) == NULL ) { perror("malloc pacctdir"); exit(11); } strcpy(pacctdir, tagvalue); /* ** verify if the atopacctd daemon is active */ if ( semget(PACCTPUBKEY, 0, 0) == -1) { fprintf(stderr, "Warning: option '%s' specified " "while atopacctd not running!\n", tagname); sleep(2); return; } /* ** verify that the topdirectory exists */ if ( stat(pacctdir, &dirstat) == -1 ) { fprintf(stderr, "Warning: option '%s' specified - ", tagname); perror(pacctdir); sleep(2); return; } if (! S_ISDIR(dirstat.st_mode) ) { fprintf(stderr, "Warning: option '%s' specified - ", tagname); fprintf(stderr, "%s not a directory\n", pacctdir); sleep(2); return; } /* ** verify that the topdirectory contains the required subdirectory */ snprintf(shadowpath, sizeof shadowpath, "%s/%s", pacctdir, PACCTSHADOWD); if ( stat(shadowpath, &dirstat) == -1 ) { fprintf(stderr, "Warning: option '%s' specified - ", tagname); perror(shadowpath); sleep(2); return; } if (! S_ISDIR(dirstat.st_mode) ) { fprintf(stderr, "Warning: option '%s' specified - ", tagname); fprintf(stderr, "%s not a directory\n", shadowpath); sleep(2); return; } }