122419Sdist /* 222419Sdist * Copyright (c) 1983 Regents of the University of California. 322419Sdist * All rights reserved. The Berkeley software License Agreement 422419Sdist * specifies the terms and conditions for redistribution. 522419Sdist */ 622419Sdist 716818Swall #ifndef lint 822419Sdist char copyright[] = 922419Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 1022419Sdist All rights reserved.\n"; 1116828Smckusick #endif not lint 1216818Swall 1322419Sdist #ifndef lint 14*28872Smckusick static char sccsid[] = "@(#)atq.c 5.2 (Berkeley) 05/28/86"; 1522419Sdist #endif not lint 1622419Sdist 1716818Swall /* 1816818Swall * 1916828Smckusick * Synopsis: atq [ -c ] [ -n ] [ name ... ] 2016818Swall * 2116818Swall * 2216828Smckusick * Print the queue of files waiting to be executed. These files 2316828Smckusick * were created by using the "at" command and are located in the 2416828Smckusick * directory "/usr/spool/at". 2516818Swall * 2616818Swall * 2716828Smckusick * Author: Steve Wall 2816828Smckusick * Computer Systems Research Group 2916828Smckusick * University of California @ Berkeley 3016818Swall * 3116818Swall */ 3216818Swall 3316818Swall # include <stdio.h> 3416818Swall # include <sys/types.h> 3516818Swall # include <sys/file.h> 3616818Swall # include <sys/dir.h> 3716818Swall # include <sys/stat.h> 3816818Swall # include <sys/time.h> 3916818Swall # include <pwd.h> 4016818Swall # include <ctype.h> 4116818Swall 4216828Smckusick # define ATDIR "/usr/spool/at" /* spooling area */ 4316828Smckusick # define LASTFILE "/usr/spool/at/lasttimedone" /* update time record 4416828Smckusick file */ 4516818Swall 4616818Swall /* 4716818Swall * Months of the year 4816818Swall */ 4916818Swall static char *mthnames[12] = { 5016828Smckusick "Jan","Feb","Mar","Apr","May","Jun","Jul", 5116828Smckusick "Aug","Sep","Oct","Nov","Dec", 5216818Swall }; 5316818Swall 54*28872Smckusick char *nullentry = NULL; /* avoid 'namelist' NULL ptr problems */ 5516828Smckusick int numentries; /* number of entries in spooling area */ 5616828Smckusick int namewanted = 0; /* only print jobs belonging to a 5716828Smckusick certain person */ 5816828Smckusick struct direct **queue; /* the queue itself */ 5916818Swall 6016818Swall 6116818Swall main(argc,argv) 6216818Swall int argc; 6316818Swall char **argv; 6416818Swall { 6516818Swall 6616828Smckusick int cflag = 0; /* print in order of creation time */ 6716828Smckusick int nflag = 0; /* just print the number of jobs in 6816828Smckusick queue */ 6916828Smckusick int usage(); /* print usage info and exit */ 7016828Smckusick int creation(); /* sort jobs by date of creation */ 7116828Smckusick int alphasort(); /* sort jobs by date of execution */ 7216828Smckusick int filewanted(); /* should a file be included in queue?*/ 7316828Smckusick int printqueue(); /* print the queue */ 7416828Smckusick int countfiles(); /* count the number of files in queue 7516828Smckusick for a given person */ 76*28872Smckusick char **namelist = &nullentry; /* array of specific name(s) requested*/ 7716818Swall 7816818Swall 7916828Smckusick --argc, ++argv; 8016818Swall 8116828Smckusick /* 8216828Smckusick * Interpret command line flags if they exist. 8316828Smckusick */ 8416828Smckusick while (argc > 0 && **argv == '-') { 8516828Smckusick (*argv)++; 8616828Smckusick while (**argv) switch (*(*argv)++) { 8716818Swall 8816828Smckusick case 'c' : cflag++; 8916828Smckusick break; 9016818Swall 9116828Smckusick case 'n' : nflag++; 9216828Smckusick break; 9316818Swall 9416828Smckusick default : usage(); 9516818Swall 9616828Smckusick } 9716828Smckusick --argc, ++argv; 9816828Smckusick } 9916818Swall 10016828Smckusick /* 10116828Smckusick * If a certain name (or names) is requested, set a pointer to the 10216828Smckusick * beginning of the list. 10316828Smckusick */ 104*28872Smckusick if (argc > 0) { 10516828Smckusick ++namewanted; 10616828Smckusick namelist = argv; 10716828Smckusick } 10816818Swall 10916828Smckusick /* 11016828Smckusick * Move to the spooling area and scan the directory, placing the 11116828Smckusick * files in the queue structure. The queue comes back sorted by 11216828Smckusick * execution time or creation time. 11316828Smckusick */ 11416828Smckusick if (chdir(ATDIR) == -1) { 11516828Smckusick perror(ATDIR); 11616828Smckusick exit(1); 11716828Smckusick } 11816828Smckusick if ((numentries = scandir(".",&queue,filewanted, (cflag) ? creation : 11916828Smckusick alphasort)) < 0) { 12016828Smckusick perror(ATDIR); 12116828Smckusick exit(1); 12216828Smckusick } 12316818Swall 12416828Smckusick /* 12516828Smckusick * Either print a message stating: 12616828Smckusick * 12716828Smckusick * 1) that the spooling area is empty. 12816828Smckusick * 2) the number of jobs in the spooling area. 12916828Smckusick * 3) the number of jobs in the spooling area belonging to 13016828Smckusick * a certain person. 13116828Smckusick * 4) that the person requested doesn't have any files in the 13216828Smckusick * spooling area. 13316828Smckusick * 13416828Smckusick * or send the queue off to "printqueue" for printing. 13516828Smckusick * 13616828Smckusick * This whole process might seem a bit elaborate, but it's worthwhile 13716828Smckusick * to print some informative messages for the user. 13816828Smckusick * 13916828Smckusick */ 14016828Smckusick if ((numentries == 0) && (!nflag)) { 14116828Smckusick printf("no files in queue.\n"); 14216828Smckusick exit(0); 14316828Smckusick } 14416828Smckusick if (nflag) { 14516828Smckusick printf("%d\n",(namewanted) ? countfiles(namelist) : numentries); 14616828Smckusick exit(0); 14716828Smckusick } 14816828Smckusick if ((namewanted) && (countfiles(namelist) == 0)) { 14916828Smckusick printf("no files for %s.\n", (argc == 1) ? 15016828Smckusick *argv : "specified users"); 15116828Smckusick exit(0); 15216828Smckusick } 15316828Smckusick printqueue(namelist); 15416828Smckusick exit(0); 15516818Swall } 15616818Swall 15716818Swall /* 15816818Swall * Count the number of jobs in the spooling area owned by a certain person(s). 15916818Swall */ 16016818Swall countfiles(namelist) 16116818Swall char **namelist; 16216818Swall { 16316828Smckusick int i; /* for loop index */ 16416828Smckusick int entryfound; /* found file owned by user(s)*/ 16516828Smckusick int numfiles = 0; /* number of files owned by a 16616828Smckusick certain person(s) */ 16716828Smckusick char **ptr; /* scratch pointer */ 16816818Swall 16916818Swall 17016828Smckusick /* 17116828Smckusick * For each file in the queue, see if the user(s) own the file. We 17216828Smckusick * have to use "entryfound" (rather than simply incrementing "numfiles") 17316828Smckusick * so that if a person's name appears twice on the command line we 17416828Smckusick * don't double the number of files owned by him/her. 17516828Smckusick */ 17616828Smckusick for (i = 0; i < numentries ; i++) { 17716828Smckusick ptr = namelist; 17816828Smckusick entryfound = 0; 17916818Swall 18016828Smckusick while (*ptr) { 18116865Swall if (isowner(*ptr,queue[i]->d_name)) 18216828Smckusick ++entryfound; 18316828Smckusick ++ptr; 18416828Smckusick } 18516828Smckusick if (entryfound) 18616828Smckusick ++numfiles; 18716828Smckusick } 18816828Smckusick return(numfiles); 18916818Swall } 19016818Swall 19116818Swall /* 19216818Swall * Print the queue. If only jobs belonging to a certain person(s) are requested, 19316818Swall * only print jobs that belong to that person(s). 19416818Swall */ 19516818Swall printqueue(namelist) 19616818Swall char **namelist; 19716818Swall { 19816828Smckusick int i; /* for loop index */ 19916828Smckusick int rank = 1; /* rank of a job */ 20016828Smckusick int entryfound; /* found file owned by user(s)*/ 20116828Smckusick int printrank(); /* print the rank of a job */ 20216828Smckusick int plastrun(); /* print the last time the 20316828Smckusick spooling area was updated */ 20416865Swall int powner(); /* print the name of the owner 20516865Swall of the job */ 20616828Smckusick int getid(); /* get uid of a person */ 20716828Smckusick char **ptr; /* scratch pointer */ 20816828Smckusick struct stat stbuf; /* buffer for file stats */ 20916818Swall 21016818Swall 21116828Smckusick /* 21216828Smckusick * Print the time the spooling area was last modified and the header 21316828Smckusick * for the queue. 21416828Smckusick */ 21516828Smckusick plastrun(); 21616828Smckusick printf(" Rank Execution Date Owner Job # Job Name\n"); 21716818Swall 21816828Smckusick /* 21916828Smckusick * Print the queue. If a certain name(s) was requested, print only jobs 22016828Smckusick * belonging to that person(s), otherwise print the entire queue. 22116828Smckusick * Once again, we have to use "entryfound" (rather than simply 22216828Smckusick * comparing each command line argument) so that if a person's name 22316828Smckusick * appears twice we don't print each file owned by him/her twice. 22416828Smckusick * 22516828Smckusick * 22616828Smckusick * "printrank", "printdate", and "printjobname" all take existing 22716828Smckusick * data and display it in a friendly manner. 22816828Smckusick * 22916828Smckusick */ 23016828Smckusick for (i = 0; i < numentries; i++) { 23116828Smckusick if ((stat(queue[i]->d_name, &stbuf)) < 0) { 23216828Smckusick continue; 23316828Smckusick } 23416828Smckusick if (namewanted) { 23516828Smckusick ptr = namelist; 23616828Smckusick entryfound = 0; 23716818Swall 23816828Smckusick while (*ptr) { 23916865Swall if (isowner(*ptr,queue[i]->d_name)) 24016828Smckusick ++entryfound; 24116828Smckusick ++ptr; 24216828Smckusick } 24316828Smckusick if (!entryfound) 24416828Smckusick continue; 24516828Smckusick } 24616828Smckusick printrank(rank++); 24716828Smckusick printdate(queue[i]->d_name); 24816865Swall powner(queue[i]->d_name); 24916828Smckusick printf("%5d",stbuf.st_ino); 25016828Smckusick printjobname(queue[i]->d_name); 25116828Smckusick } 25216828Smckusick ++ptr; 25316818Swall } 25416818Swall 25516818Swall /* 25616865Swall * See if "name" owns "job". 25716818Swall */ 25816865Swall isowner(name,job) 25916865Swall char *name; 26016865Swall char *job; 26116818Swall { 262*28872Smckusick char buf[128]; /* buffer for 1st line of spoolfile 26316865Swall header */ 26416865Swall FILE *infile; /* I/O stream to spoolfile */ 26516818Swall 26616865Swall if ((infile = fopen(job,"r")) == NULL) { 267*28872Smckusick fprintf(stderr,"Couldn't open spoolfile "); 26816865Swall perror(job); 26916865Swall return(0); 27016828Smckusick } 27116865Swall 272*28872Smckusick if (fscanf(infile,"# owner: %127s%*[^\n]\n",buf) != 1) { 27316865Swall fclose(infile); 27416865Swall return(0); 27516865Swall } 27616865Swall 27716865Swall fclose(infile); 27816865Swall return((strcmp(name,buf) == 0) ? 1 : 0); 27916818Swall } 28016865Swall 28116865Swall /* 28216865Swall * Print the owner of the job. This is stored on the first line of the 28316865Swall * spoolfile. If we run into trouble getting the name, we'll just print "???". 28416865Swall */ 28516865Swall powner(file) 28616865Swall char *file; 28716865Swall { 288*28872Smckusick char owner[10]; /* the owner */ 28916865Swall FILE *infile; /* I/O stream to spoolfile */ 29016865Swall 29116865Swall /* 29216865Swall * Open the job file and grab the first line. 29316865Swall */ 29416865Swall 29516865Swall if ((infile = fopen(file,"r")) == NULL) { 29616865Swall printf("%-10.9s","???"); 297*28872Smckusick perror(file); 29816865Swall return; 29916865Swall } 30016865Swall 301*28872Smckusick if (fscanf(infile,"# owner: %9s%*[^\n]\n",owner) != 1) { 30216865Swall printf("%-10.9s","???"); 30316865Swall fclose(infile); 30416865Swall return; 30516865Swall } 30616865Swall 30716865Swall fclose(infile); 30816865Swall printf("%-10.9s",owner); 30916865Swall 31016865Swall } 31116828Smckusick 31216865Swall 31316818Swall /* 31416818Swall * Get the uid of a person using his/her login name. Return -1 if no 31516818Swall * such account name exists. 31616818Swall */ 31716818Swall getid(name) 31816818Swall char *name; 31916818Swall { 32016818Swall 32116828Smckusick struct passwd *pwdinfo; /* password info structure */ 32216818Swall 32316818Swall 32416828Smckusick if ((pwdinfo = getpwnam(name)) == 0) 32516828Smckusick return(-1); 32616818Swall 32716828Smckusick return(pwdinfo->pw_uid); 32816818Swall } 32916818Swall 33016818Swall /* 33116818Swall * Print the time the spooling area was updated. 33216818Swall */ 33316818Swall plastrun() 33416818Swall { 33516828Smckusick struct timeval now; /* time it is right now */ 33616828Smckusick struct timezone zone; /* NOT USED */ 33716828Smckusick struct tm *loc; /* detail of time it is right */ 33816828Smckusick u_long lasttime; /* last update time in seconds 33916828Smckusick since 1/1/70 */ 34016828Smckusick FILE *last; /* file where last update hour 34116828Smckusick is stored */ 34216818Swall 34316818Swall 34416828Smckusick /* 34516828Smckusick * Open the file where the last update time is stored, and grab the 34616828Smckusick * last update hour. The update time is measured in seconds since 34716828Smckusick * 1/1/70. 34816828Smckusick */ 34916828Smckusick if ((last = fopen(LASTFILE,"r")) == NULL) { 35016828Smckusick perror(LASTFILE); 35116828Smckusick exit(1); 35216828Smckusick } 35316828Smckusick fscanf(last,"%d",(u_long) &lasttime); 35416828Smckusick fclose(last); 35516818Swall 35616828Smckusick /* 35716828Smckusick * Get a broken down representation of the last update time. 35816828Smckusick */ 35916828Smckusick loc = localtime(&lasttime); 36016818Swall 36116828Smckusick /* 36216828Smckusick * Print the time that the spooling area was last updated. 36316828Smckusick */ 36416828Smckusick printf("\n LAST EXECUTION TIME: %s ",mthnames[loc->tm_mon]); 36516828Smckusick printf("%d, 19%d ",loc->tm_mday,loc->tm_year); 36616828Smckusick printf("at %d:%02d\n\n",loc->tm_hour,loc->tm_min); 36716818Swall } 36816818Swall 36916818Swall /* 37016818Swall * Print the rank of a job. (I've got to admit it, I stole it from "lpq") 37116818Swall */ 37216818Swall static 37316818Swall printrank(n) 37416818Swall { 37516828Smckusick static char *r[] = { 37616828Smckusick "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 37716828Smckusick }; 37816818Swall 37916828Smckusick if ((n/10) == 1) 38016828Smckusick printf("%3d%-5s", n,"th"); 38116828Smckusick else 38216828Smckusick printf("%3d%-5s", n, r[n%10]); 38316818Swall } 38416818Swall 38516818Swall /* 38616818Swall * Print the date that a job is to be executed. This takes some manipulation 38716818Swall * of the file name. 38816818Swall */ 38916818Swall printdate(filename) 39016818Swall char *filename; 39116818Swall { 39216828Smckusick int yday = 0; /* day of year file will be 39316828Smckusick executed */ 39416828Smckusick int min = 0; /* min. file will be executed */ 39516828Smckusick int hour = 0; /* hour file will be executed */ 39616828Smckusick int day = 0; /* day file will be executed */ 39716828Smckusick int month = 0; /* month file will be executed*/ 39816828Smckusick int year = 0; /* year file will be executed */ 39916828Smckusick int get_mth_day(); /* convert a day of year to a 40016828Smckusick month and day of month */ 40116828Smckusick char date[18]; /* reformatted execution date */ 40216818Swall 40316828Smckusick /* 40416828Smckusick * Pick off the necessary info from the file name and convert the day 40516828Smckusick * of year to a month and day of month. 40616828Smckusick */ 40716828Smckusick sscanf(filename,"%2d.%3d.%2d%2d",&year,&yday,&hour,&min); 40816828Smckusick get_mth_day(year,yday,&month,&day); 40916818Swall 41016828Smckusick /* 41116828Smckusick * Format the execution date of a job. 41216828Smckusick */ 41316828Smckusick sprintf(date,"%3s %2d, 19%2d %02d:%02d",mthnames[month], 41416828Smckusick day, year,hour,min); 41516818Swall 41616828Smckusick /* 41716828Smckusick * Print the date the job will be executed. 41816828Smckusick */ 41916828Smckusick printf("%-21.18s",date); 42016818Swall } 42116818Swall 42216818Swall /* 42316818Swall * Given a day of the year, calculate the month and day of month. 42416818Swall */ 42516818Swall get_mth_day(year,dayofyear,month,day) 42616818Swall int year, dayofyear, *month, *day; 42716818Swall 42816818Swall { 42916818Swall 43016828Smckusick int i = 1; /* for loop index */ 43116828Smckusick int leap; /* are we dealing with a leap 43216828Smckusick year? */ 43316828Smckusick /* Table of the number of days 43416828Smckusick in each month of the year. 43516818Swall 43616828Smckusick dofy_tab[1] -- regular year 43716828Smckusick dofy_tab[2] -- leap year 43816828Smckusick */ 43916818Swall 44016828Smckusick static int dofy_tab[2][13] = { 44116828Smckusick { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 44216828Smckusick { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 44316828Smckusick }; 44416818Swall 44516828Smckusick /* 44616828Smckusick * Are we dealing with a leap year? 44716828Smckusick */ 44816828Smckusick leap = ((year%4 == 0 && year%100 != 0) || year%100 == 0); 44916818Swall 45016828Smckusick /* 45116828Smckusick * Calculate the month of the year and day of the month. 45216828Smckusick */ 45316828Smckusick while (dayofyear >= dofy_tab[leap][i]) { 45416828Smckusick dayofyear -= dofy_tab[leap][i++]; 45516828Smckusick ++(*month); 45616828Smckusick } 45716828Smckusick *day = (dayofyear + 1); 45816818Swall } 45916828Smckusick 46016818Swall /* 46116818Swall * Print a job name. If the old "at" has been used to create the spoolfile, 46216818Swall * the three line header that the new version of "at" puts in the spoolfile. 46316818Swall * Thus, we just print "???". 46416818Swall */ 46516818Swall printjobname(file) 46616818Swall char *file; 46716818Swall { 46816865Swall char *ptr; /* scratch pointer */ 469*28872Smckusick char jobname[28]; /* the job name */ 47016828Smckusick FILE *filename; /* job file in spooling area */ 47116818Swall 47216828Smckusick /* 47316865Swall * Open the job file and grab the second line. 47416828Smckusick */ 47516828Smckusick printf(" "); 47616818Swall 47716828Smckusick if ((filename = fopen(file,"r")) == NULL) { 47816828Smckusick printf("%.27s\n", "???"); 479*28872Smckusick perror(file); 48016828Smckusick return; 48116828Smckusick } 48216865Swall /* 483*28872Smckusick * Skip over the first line. 48416865Swall */ 485*28872Smckusick fscanf(filename,"%*[^\n]\n"); 48616865Swall 48716865Swall /* 48816865Swall * Now get the job name. 48916865Swall */ 490*28872Smckusick if (fscanf(filename,"# jobname: %27s%*[^\n]\n",jobname) != 1) { 49116828Smckusick printf("%.27s\n", "???"); 49216828Smckusick fclose(filename); 49316828Smckusick return; 49416828Smckusick } 49516828Smckusick fclose(filename); 49616818Swall 49716828Smckusick /* 49816828Smckusick * Put a pointer at the begining of the line and remove the basename 49916828Smckusick * from the job file. 50016828Smckusick */ 50116865Swall ptr = jobname; 50216865Swall if ((ptr = (char *)rindex(jobname,'/')) != 0) 50316865Swall ++ptr; 50416828Smckusick else 50516865Swall ptr = jobname; 50616818Swall 50716865Swall if (strlen(ptr) > 23) 50816865Swall printf("%.23s ...\n",ptr); 50916865Swall else 51016865Swall printf("%.27s\n",ptr); 51116818Swall } 51216818Swall 51316818Swall /* 51416818Swall * Do we want to include a file in the queue? (used by "scandir") We are looking 51516818Swall * for files with following syntax: yy.ddd.hhhh. so the test is made to see if 51616818Swall * the file name has three dots in it. This test will suffice since the only 51716818Swall * other files in /usr/spool/at don't have any dots in their name. 51816818Swall */ 51916818Swall filewanted(direntry) 52016818Swall struct direct *direntry; 52116818Swall { 52216828Smckusick int numdot = 0; 52316828Smckusick char *filename; 52416818Swall 52516828Smckusick filename = direntry->d_name; 52616828Smckusick while (*filename) 52716828Smckusick numdot += (*(filename++) == '.'); 52816828Smckusick return(numdot == 3); 52916818Swall } 53016818Swall 53116818Swall /* 53216818Swall * Sort files by time of creation. (used by "scandir") 53316818Swall */ 53416818Swall creation(d1, d2) 53516818Swall struct direct **d1, **d2; 53616818Swall { 53716828Smckusick struct stat stbuf1, stbuf2; 53816818Swall 53916828Smckusick if (stat((*d1)->d_name,&stbuf1) < 0) 54016828Smckusick return(1); 54116818Swall 54216828Smckusick if (stat((*d2)->d_name,&stbuf2) < 0) 54316828Smckusick return(1); 54416818Swall 54516828Smckusick return(stbuf1.st_ctime < stbuf2.st_ctime); 54616818Swall } 54716828Smckusick 54816818Swall /* 54916818Swall * Print usage info and exit. 55016818Swall */ 55116818Swall usage() 55216818Swall { 55316828Smckusick fprintf(stderr,"usage: atq [-c] [-n] [name ...]\n"); 55416828Smckusick exit(1); 55516818Swall } 556