1: /* 2: * Steven Schultz - sms@moe.2bsd.com 3: * 4: * @(#)accton.c 1.1 (2.11BSD) 1999/5/5 5: * 6: * accton - enable/disable process accounting. 7: */ 8: 9: #include <signal.h> 10: #include <stdio.h> 11: #include <errno.h> 12: #include <stdlib.h> 13: #include <sys/types.h> 14: #include <sys/acct.h> 15: #include <sys/stat.h> 16: 17: static int Suspend = 2; /* %free when accounting suspended */ 18: static int Resume = 4; /* %free when accounting to be resumed */ 19: static int Chkfreq = 30; /* how often (seconds) to check disk space */ 20: static char *Acctfile; 21: extern char *__progname; 22: void usage(); 23: 24: main(argc, argv) 25: int argc; 26: char **argv; 27: { 28: int c; 29: pid_t pid; 30: char *cffile = _PATH_ACCTDCF; 31: char *pidfile = _PATH_ACCTDPID; 32: char *cp; 33: long l; 34: register FILE *fp; 35: int status; 36: struct stat st; 37: 38: if (getuid()) 39: errx(1, "Only root can run this program"); 40: /* 41: * Handle the simple case of no arguments at all. This turns off accounting 42: * completely by killing the accounting daemon 'acctd'. 43: */ 44: if (argc == 1) 45: { 46: fp = fopen(pidfile, "r"); 47: /* 48: * Note: it is not fatal to fail opening the pid file. The accounting daemon 49: * may not have been run on this system yet. 50: */ 51: if (!fp) 52: exit(0); 53: if (fscanf(fp, "%d\n", &pid) != 1) 54: errx(1, "%s corrupt/unparseable", 55: pidfile); 56: if (pid < 3 || pid > 30000) /* Paranoia */ 57: errx(1, "%s content out of bound(30000>pid>3)", 58: pidfile); 59: fclose(fp); 60: if (kill(pid, SIGTERM) < 0) 61: { 62: /* 63: * It is not an error to turn off accounting if it is already disabled. Ignore 64: * the no such process error from kill. 65: */ 66: if (errno != ESRCH) 67: err(1, "kill(%d,SIGTERM)", pid); 68: } 69: exit(0); 70: } 71: 72: while ((c = getopt(argc, argv, "f:r:s:t:")) != EOF) 73: { 74: switch (c) 75: { 76: case 'f': 77: Acctfile = optarg; 78: break; 79: case 'r': 80: cp = NULL; 81: l = strtol(optarg, &cp, 10); 82: if (l < 0 || l > 99 || (cp && *cp)) 83: errx(1, "bad -r value"); 84: Resume = (int)l; 85: break; 86: case 's': 87: cp = NULL; 88: l = strtol(optarg, &cp, 10); 89: if (l < 0 || l > 99 || (cp && *cp)) 90: errx(1, "bad -s value"); 91: Suspend = (int)l; 92: break; 93: case 't': 94: cp = NULL; 95: l = strtol(optarg, &cp, 10); 96: if (l < 5 || l > 3600 || (cp && *cp)) 97: errx(1, "bad -t value (3600>=t>=5"); 98: Chkfreq = (int)l; 99: break; 100: case '?': 101: default: 102: usage(); 103: /* NOTREACHED */ 104: } 105: } 106: argc -= optind; 107: argv += optind; 108: /* 109: * If we have exactly one argument left then it must be a filename. This 110: * preserves the historical practice of running "accton /usr/adm/acct" to 111: * enable accounting. It is an error to have more than one argument at this 112: * point. 113: */ 114: if (argc == 1) 115: { 116: if (Acctfile) 117: warnx("'-f %s' being overridden by trailing '%s'", 118: Acctfile, *argv); 119: Acctfile = *argv; 120: } 121: else if (argc != 0) 122: { 123: usage(); 124: /* NOTREACHED */ 125: } 126: /* 127: * If after all of that we still don't have a file name then use the 128: * default one. 129: */ 130: if (!Acctfile) 131: Acctfile = _PATH_ACCTFILE; 132: 133: /* 134: * Now open the conf file (creating it if it does not exist) and write out 135: * the parameters for the accounting daemon. The conf file format is simple: 136: * "tag=value\n". NO EXTRA WHITE SPACE or COMMENTS! 137: * 138: * IF the file already exists it must be owned by root and not writeable by 139: * group or other. This is because the conf file contains a pathname that 140: * will be trusted by a daemon running as root. The same check is made by 141: * 'acctd' to prevent believing bogus information. 142: */ 143: if (stat(cffile, &st) == 0) 144: { 145: if (st.st_uid != 0) 146: errx(1, "%s not owned by root", cffile); 147: if (st.st_mode & (S_IWGRP|S_IWOTH)) 148: errx(1, "%s writeable by group/other", cffile); 149: } 150: fp = fopen(cffile, "w"); 151: if (!fp) 152: errx(1, "fopen(%s,w)", cffile); 153: fprintf(fp, "suspend=%d\n", Suspend); 154: fprintf(fp, "resume=%d\n", Resume); 155: fprintf(fp, "chkfreq=%d\n", Chkfreq); 156: fprintf(fp, "acctfile=%s\n", Acctfile); 157: fclose(fp); 158: 159: /* 160: * After writing the conf file we need to send the current running 'acctd' 161: * process (if any) a SIGHUP. If the pidfile can not be opened this may be 162: * the first time 'acctd' has ever been run. 163: */ 164: fp = fopen(pidfile, "r"); 165: if (fp) 166: { 167: if (fscanf(fp, "%d\n", &pid) != 1) 168: errx(1, "%s corrupt/unparseable", pidfile); 169: if (pid < 3 || pid > 30000) /* Paranoia */ 170: errx(1, "%s content out of bound(30000>pid>3)", 171: pidfile); 172: fclose(fp); 173: /* 174: * If the signal can be successfully posted to the process then do not 175: * attempt to start another instance of acctd (it will fail but syslog 176: * an annoying error message). If the signal can not be posted but the 177: * acctd process does not exist then go start it. Otherwise complain. 178: */ 179: if (kill(pid, SIGHUP) < 0) 180: { 181: if (errno != ESRCH) 182: err(1, "%d from %s bogus value", pid, pidfile); 183: /* process no longer exists, fall thru and start it */ 184: } 185: else 186: exit(0); 187: } 188: pid = vfork(); 189: switch (pid) 190: { 191: case -1: 192: err(1, "vfork"); 193: /* NOTREACHED */ 194: case 0: /* Child process */ 195: if (execl(_PATH_ACCTD, "acctd", NULL) < 0) 196: err(1, "execl %s", _PATH_ACCTD); 197: /* NOTREACHED */ 198: default: /* Parent */ 199: break; 200: } 201: if (waitpid(pid, &status, 0) < 0) 202: err(1, "waitpid for %d", pid); 203: exit(0); 204: } 205: 206: void 207: usage() 208: { 209: 210: errx(1,"Usage: %s [-f acctfile] [-s %suspend] [-r %resume] [-t chkfreq] [acctfile]", __progname); 211: /* NOTREACHED */ 212: }