148234Sbostic /*-
2*61913Sbostic * Copyright (c) 1983, 1993
3*61913Sbostic * The Regents of the University of California. All rights reserved.
448234Sbostic *
548234Sbostic * %sccs.include.proprietary.c%
622420Sdist */
722420Sdist
816820Swall #ifndef lint
9*61913Sbostic static char copyright[] =
10*61913Sbostic "@(#) Copyright (c) 1983, 1993\n\
11*61913Sbostic The Regents of the University of California. All rights reserved.\n";
1248234Sbostic #endif /* not lint */
1316820Swall
1422420Sdist #ifndef lint
15*61913Sbostic static char sccsid[] = "@(#)atrm.c 8.1 (Berkeley) 06/06/93";
1648234Sbostic #endif /* not lint */
1722420Sdist
1816820Swall /*
1916829Smckusick * synopsis: atrm [-f] [-i] [-] [[job #] [user] ...]
2016820Swall *
2116820Swall *
2216829Smckusick * Remove files from the directory /usr/spool/at. These files
2316829Smckusick * represent jobs to be run at a later date.
2416820Swall *
2516829Smckusick * Author: Steve Wall
2616829Smckusick * Computer Systems Research Group
2716829Smckusick * University of California @ Berkeley
2816820Swall *
2916820Swall */
3016820Swall
3116820Swall #include <sys/types.h>
3216820Swall #include <sys/dir.h>
3316820Swall #include <sys/file.h>
3416820Swall #include <sys/stat.h>
3537347Sbostic #include <stdio.h>
3637347Sbostic #include <pwd.h>
3737347Sbostic #include <ctype.h>
3837962Sbostic #include "pathnames.h"
3916820Swall
4016829Smckusick #define SUPERUSER 0 /* is user super-user? */
4116829Smckusick #define MAXENTRIES 1000 /* max # of entries allowed */
4216820Swall
4316829Smckusick int user; /* person requesting removal */
4416829Smckusick int fflag = 0; /* suppress announcements? */
4516829Smckusick int iflag = 0; /* run interactively? */
4616820Swall
main(argc,argv)4716820Swall main(argc,argv)
4816820Swall int argc;
4916820Swall char **argv;
5016820Swall
5116820Swall {
5230410Skarels register int i; /* for loop index */
5316829Smckusick int isuname; /* is a command line argv a user name?*/
5416829Smckusick int numjobs; /* # of jobs in spooling area */
5516829Smckusick int usage(); /* print usage info and exit */
5616829Smckusick int allflag = 0; /* remove all jobs belonging to user? */
5730410Skarels int jobno;
5816829Smckusick int jobexists; /* does a requested job exist? */
5916829Smckusick int alphasort(); /* sort jobs by date of execution */
6016829Smckusick int filewanted(); /* should a file be listed in queue? */
6130410Skarels char *myname, *getname(); /* current user's name */
6230410Skarels char *owner, *fowner();
6316829Smckusick struct stat *statptr; /* pointer to file stat structure */
6416829Smckusick struct stat *stbuf[MAXENTRIES]; /* array of pointers to stat structs */
6516829Smckusick struct direct **namelist; /* names of jobs in spooling area */
6616820Swall
6716820Swall
6816829Smckusick /*
6916829Smckusick * If job number, user name, or "-" is not specified, just print
7016829Smckusick * usage info and exit.
7116829Smckusick */
7216829Smckusick if (argc < 2)
7316829Smckusick usage();
7416820Swall
7516829Smckusick --argc; ++argv;
7616820Swall
7716829Smckusick /*
7816829Smckusick * Process command line flags.
7928873Smckusick * Special case the "-" option so that others may be grouped.
8016829Smckusick */
8128873Smckusick while (argc > 0 && **argv == '-') {
8228873Smckusick if (*(++(*argv)) == '\0') {
8328873Smckusick ++allflag;
8428873Smckusick } else while (**argv) switch (*(*argv)++) {
8516820Swall
8616829Smckusick case 'f': ++fflag;
8716829Smckusick break;
8816829Smckusick
8916829Smckusick case 'i': ++iflag;
9016829Smckusick break;
9116829Smckusick
9216829Smckusick default: usage();
9316829Smckusick }
9416829Smckusick ++argv; --argc;
9516829Smckusick }
9616820Swall
9716829Smckusick /*
9816829Smckusick * If all jobs are to be removed and extra command line arguments
9916829Smckusick * are given, print usage info and exit.
10016829Smckusick */
10116829Smckusick if (allflag && argc)
10216829Smckusick usage();
10316820Swall
10416829Smckusick /*
10516829Smckusick * If only certain jobs are to be removed and no job #'s or user
10616829Smckusick * names are specified, print usage info and exit.
10716829Smckusick */
10816829Smckusick if (!allflag && !argc)
10916829Smckusick usage();
11016820Swall
11116829Smckusick /*
11216829Smckusick * If interactive removal and quiet removal are requested, override
11316829Smckusick * quiet removal and run interactively.
11416829Smckusick */
11516829Smckusick if (iflag && fflag)
11616829Smckusick fflag = 0;
11716820Swall
11816829Smckusick /*
11916829Smckusick * Move to spooling area and get user id of person requesting removal.
12016829Smckusick */
12137962Sbostic if (chdir(_PATH_ATDIR) == -1) {
12237962Sbostic perror(_PATH_ATDIR);
12316829Smckusick exit(1);
12416829Smckusick }
12516829Smckusick user = getuid();
12630410Skarels myname = getname(user);
12716820Swall
12816829Smckusick /*
12916829Smckusick * Get a list of the files in the spooling area.
13016829Smckusick */
13116866Swall if ((numjobs = scandir(".",&namelist,filewanted,alphasort)) < 0) {
13237962Sbostic perror(_PATH_ATDIR);
13316829Smckusick exit(1);
13416829Smckusick }
13516820Swall
13616829Smckusick /*
13716829Smckusick * Build an array of pointers to the file stats for all jobs in
13816829Smckusick * the spooling area.
13916829Smckusick */
14016829Smckusick for (i = 0; i < numjobs; ++i) {
14116829Smckusick statptr = (struct stat *) malloc(sizeof(struct stat));
14216829Smckusick if (statptr == NULL) {
14316829Smckusick perror("malloc");
14416829Smckusick exit(1);
14516829Smckusick }
14616829Smckusick if (stat(namelist[i]->d_name,statptr) < 0) {
14716829Smckusick perror("stat");
14816829Smckusick continue;
14916829Smckusick }
15016829Smckusick stbuf[i] = statptr;
15116829Smckusick }
15216820Swall
15316829Smckusick /*
15416829Smckusick * If all jobs belonging to the user are to be removed, compare
15516829Smckusick * the user's id to the owner of the file. If they match, remove
15616829Smckusick * the file. If the user is the super-user, don't bother comparing
15716829Smckusick * the id's. After all files are removed, exit (status 0).
15816829Smckusick */
15916829Smckusick if (allflag) {
16016829Smckusick for (i = 0; i < numjobs; ++i) {
16130410Skarels owner = fowner(namelist[i]->d_name);
16230410Skarels if (isowner(myname, owner))
16330410Skarels removentry(namelist[i]->d_name,
16430410Skarels (int)stbuf[i]->st_ino, NULL);
16516829Smckusick }
16616829Smckusick exit(0);
16716829Smckusick }
16816820Swall
16916829Smckusick /*
17016829Smckusick * If only certain jobs are to be removed, interpret each command
17116829Smckusick * line argument. A check is done to see if it is a user's name or
17216829Smckusick * a job number (inode #). If it's a user's name, compare the argument
17316829Smckusick * to the files owner. If it's a job number, compare the argument to
17416829Smckusick * the inode number of the file. In either case, if a match occurs,
17516829Smckusick * try to remove the file. (The function "isusername" scans the
17616829Smckusick * argument to see if it is all digits which we will assume means
17716829Smckusick * that it's a job number (a fairly safe assumption?). This is done
17816829Smckusick * because we have to determine whether we are dealing with a user
17916829Smckusick * name or a job number. By assuming that only arguments that are
18016829Smckusick * all digits is a job number, we allow users to have digits in
18116829Smckusick * their login name i.e. "johndoe2").
18216829Smckusick */
18316820Swall
18416829Smckusick while (argc--) {
18516829Smckusick jobexists = 0;
18616829Smckusick isuname = isusername(*argv);
18730410Skarels if (!isuname)
18830410Skarels jobno = atoi(*argv);
18916829Smckusick for (i = 0; i < numjobs; ++i) {
19016820Swall
19128873Smckusick /* if the inode number is 0, this entry was removed */
19228873Smckusick if (stbuf[i]->st_ino == 0)
19328873Smckusick continue;
19428873Smckusick
19530410Skarels owner = fowner(namelist[i]->d_name);
19616829Smckusick /*
19730410Skarels * if argv is a username, compare it to
19830410Skarels * the owner of the file......
19930410Skarels * otherwise, we assume that the argv is a job # and
20030410Skarels * thus compare argv to the inode (job #) of the file.
20116829Smckusick */
20216829Smckusick if (isuname) {
20330410Skarels if (strcmp(*argv, owner))
20416829Smckusick continue;
20516829Smckusick } else {
20630410Skarels if (stbuf[i]->st_ino != jobno)
20716829Smckusick continue;
20816829Smckusick }
20916829Smckusick ++jobexists;
21028873Smckusick /*
21130410Skarels * if the entry is removed, don't
21228873Smckusick * try to remove it again later.
21328873Smckusick */
21430410Skarels if (user == SUPERUSER || isowner(myname, owner)) {
21530410Skarels removentry(namelist[i]->d_name,
21630410Skarels (int)stbuf[i]->st_ino, owner);
21728873Smckusick stbuf[i]->st_ino = 0;
21830410Skarels } else if (!fflag)
21930410Skarels printf("%6d: permission denied\n",
22030410Skarels stbuf[i]->st_ino);
22130410Skarels if (!isuname)
22230410Skarels break;
22316829Smckusick }
22416820Swall
22516829Smckusick /*
22616829Smckusick * If a requested argument doesn't exist, print a message.
22716829Smckusick */
22816829Smckusick if (!jobexists && !fflag && !isuname) {
22928873Smckusick fprintf(stderr, "%6s: no such job number\n", *argv);
23016829Smckusick }
23116829Smckusick ++argv;
23216829Smckusick }
23316829Smckusick exit(0);
23416820Swall }
23516820Swall
23616820Swall /*
23716820Swall * Print usage info and exit.
23816820Swall */
usage()23916820Swall usage()
24016820Swall {
24116829Smckusick fprintf(stderr,"usage: atrm [-f] [-i] [-] [[job #] [user] ...]\n");
24216829Smckusick exit(1);
24316820Swall }
24416820Swall
24516820Swall /*
24616820Swall * Do we want to include a file in the queue? (used by "scandir") We are looking
24716820Swall * for files with following syntax: yy.ddd.hhhh. so the test is made to see if
24816820Swall * the file name has three dots in it. This test will suffice since the only
24916820Swall * other files in /usr/spool/at don't have any dots in their name.
25016820Swall */
25116820Swall filewanted(direntry)
25216820Swall struct direct *direntry;
25316820Swall {
25416829Smckusick int numdot = 0; /* number of dots in a filename */
25516829Smckusick char *filename; /* filename we are looking at */
25616820Swall
25716829Smckusick filename = direntry->d_name;
25816829Smckusick while (*filename)
25916829Smckusick numdot += (*(filename++) == '.');
26016829Smckusick return(numdot == 3);
26116820Swall }
26216820Swall
26316820Swall /*
26416820Swall * Is a command line argument a username? As noted above we will assume
26516820Swall * that an argument that is all digits means that it's a job number, not
26616820Swall * a user's name. We choose to determine whether an argument is a user name
26716820Swall * in this manner because then it's ok for someone to have digits in their
26816820Swall * user name.
26916820Swall */
isusername(string)27016820Swall isusername(string)
27116820Swall char *string;
27216820Swall {
27316829Smckusick char *ptr; /* pointer used for scanning string */
27416820Swall
27516829Smckusick ptr = string;
27616829Smckusick while (isdigit(*ptr))
27716829Smckusick ++ptr;
27816829Smckusick return((*ptr == '\0') ? 0 : 1);
27916820Swall }
28016820Swall
28116820Swall /*
28216820Swall * Remove an entry from the queue. The access of the file is checked for
28316820Swall * write permission (since all jobs are mode 644). If access is granted,
28416820Swall * unlink the file. If the fflag (suppress announcements) is not set,
28516820Swall * print the job number that we are removing and the result of the access
28616866Swall * check (either "permission denied" or "removed"). If we are running
28716866Swall * interactively (iflag), prompt the user before we unlink the file. If
28816866Swall * the super-user is removing jobs, inform him/her who owns each file before
28930410Skarels * it is removed.
29016820Swall */
removentry(filename,inode,owner)29130410Skarels removentry(filename, inode, owner)
29216820Swall char *filename;
29316820Swall int inode;
29430410Skarels char *owner;
29516820Swall {
29616820Swall
29716829Smckusick if (!fflag)
29816829Smckusick printf("%6d: ",inode);
29916820Swall
30030410Skarels if (iflag) {
30130410Skarels if (user == SUPERUSER && owner)
30230410Skarels printf("\t(owned by %s) ", owner);
30330410Skarels printf("remove? ");
30430410Skarels if (!yes())
30530410Skarels return;
30616829Smckusick }
30730410Skarels if (unlink(filename) < 0)
30830410Skarels perror(filename);
30930410Skarels else if (!fflag && !iflag)
31030410Skarels printf("removed\n");
31116820Swall }
31216820Swall
31316820Swall /*
31430410Skarels * See if "name" owns job owned by "jobname".
31516866Swall */
isowner(name,jobname)31630410Skarels isowner(name,jobname)
31716866Swall char *name;
31830410Skarels char *jobname;
31916866Swall {
32016866Swall
32130410Skarels return (strcmp(name,jobname) == 0);
32216866Swall }
32316866Swall
32416866Swall /*
32530410Skarels * Return the owner of the job. This is stored on the first line of the
32630410Skarels * spoolfile. If we run into trouble getting the name, we'll just return "???".
32716866Swall */
32830410Skarels char *
fowner(file)32930410Skarels fowner(file)
33016866Swall char *file;
33116866Swall {
33230410Skarels static char owner[128]; /* the owner */
33316866Swall FILE *infile; /* I/O stream to spoolfile */
33416866Swall
33516866Swall /*
33616866Swall * Open the job file and grab the first line.
33716866Swall */
33816866Swall
33916866Swall if ((infile = fopen(file,"r")) == NULL) {
34016866Swall perror(file);
34130410Skarels return ("???");
34216866Swall }
34316866Swall
34428873Smckusick if (fscanf(infile,"# owner: %127s%*[^\n]\n",owner) != 1) {
34516866Swall fclose(infile);
34630410Skarels return ("???");
34716866Swall }
34816866Swall
34916866Swall fclose(infile);
35030410Skarels return (owner);
35116866Swall
35216866Swall }
35316866Swall
35416866Swall /*
35516820Swall * Get answer to interactive prompts, eating all characters beyond the first
35616820Swall * one. If a 'y' is typed, return 1.
35716820Swall */
yes()35816820Swall yes()
35916820Swall {
36016829Smckusick char ch; /* dummy variable */
36116829Smckusick char ch1; /* dummy variable */
36216820Swall
36316829Smckusick ch = ch1 = getchar();
36416829Smckusick while (ch1 != '\n' && ch1 != EOF)
36516829Smckusick ch1 = getchar();
36628873Smckusick if (isupper(ch))
36728873Smckusick ch = tolower(ch);
36816829Smckusick return(ch == 'y');
36916820Swall }
37016820Swall
37116820Swall /*
37216820Swall * Get the uid of a person using his/her login name. Return -1 if no
37316820Swall * such account name exists.
37416820Swall */
getid(name)37516820Swall getid(name)
37616820Swall char *name;
37716820Swall {
37816820Swall
37916829Smckusick struct passwd *pwdinfo; /* password info structure */
38016829Smckusick
38116829Smckusick if ((pwdinfo = getpwnam(name)) == 0)
38216829Smckusick return(-1);
38316820Swall
38416829Smckusick return(pwdinfo->pw_uid);
38516820Swall }
38616820Swall
38716820Swall /*
38816820Swall * Get the full login name of a person using his/her user id.
38916820Swall */
39016820Swall char *
getname(uid)39116820Swall getname(uid)
39216820Swall int uid;
39316820Swall {
39416829Smckusick struct passwd *pwdinfo; /* password info structure */
39530410Skarels char *logname, *getlogin();
39616829Smckusick
39716820Swall
39830410Skarels logname = getlogin();
39930410Skarels if (logname == NULL || (pwdinfo = getpwnam(logname)) == NULL ||
40030410Skarels pwdinfo->pw_uid != uid)
40130410Skarels pwdinfo = getpwuid(uid);
40230410Skarels if (pwdinfo == 0) {
40330410Skarels fprintf(stderr, "no name for uid %d?\n", uid);
40430410Skarels exit(1);
40530410Skarels }
40616829Smckusick return(pwdinfo->pw_name);
40716820Swall }
408