1*16818Swall #ifndef lint 2*16818Swall static char sccsid[] = "@(#)atq.c 4.1 (Berkeley) 5/25/84"; 3*16818Swall #endif 4*16818Swall 5*16818Swall /* 6*16818Swall * 7*16818Swall * Synopsis: atq [ -c ] [ -n ] [ name ... ] 8*16818Swall * 9*16818Swall * 10*16818Swall * Print the queue of files waiting to be executed. These files 11*16818Swall * were created by using the "at" command and are located in the 12*16818Swall * directory "/usr/spool/at". 13*16818Swall * 14*16818Swall * 15*16818Swall * Author: Steve Wall 16*16818Swall * Computer Systems Research Group 17*16818Swall * University of California @ Berkeley 18*16818Swall * 19*16818Swall */ 20*16818Swall 21*16818Swall # include <stdio.h> 22*16818Swall # include <sys/types.h> 23*16818Swall # include <sys/file.h> 24*16818Swall # include <sys/dir.h> 25*16818Swall # include <sys/stat.h> 26*16818Swall # include <sys/time.h> 27*16818Swall # include <pwd.h> 28*16818Swall # include <ctype.h> 29*16818Swall 30*16818Swall # define ATDIR "/usr/spool/at" /* spooling area */ 31*16818Swall # define LASTFILE "/usr/spool/at/lasttimedone" /* update time record 32*16818Swall file */ 33*16818Swall 34*16818Swall /* 35*16818Swall * Months of the year 36*16818Swall */ 37*16818Swall static char *mthnames[12] = { 38*16818Swall "Jan","Feb","Mar","Apr","May","Jun","Jul", 39*16818Swall "Aug","Sep","Oct","Nov","Dec", 40*16818Swall }; 41*16818Swall 42*16818Swall 43*16818Swall int numentries; /* number of entries in spooling area */ 44*16818Swall int namewanted = 0; /* only print jobs belonging to a 45*16818Swall certain person */ 46*16818Swall struct direct **queue; /* the queue itself */ 47*16818Swall 48*16818Swall 49*16818Swall main(argc,argv) 50*16818Swall int argc; 51*16818Swall char **argv; 52*16818Swall { 53*16818Swall 54*16818Swall int cflag = 0; /* print in order of creation time */ 55*16818Swall int nflag = 0; /* just print the number of jobs in 56*16818Swall queue */ 57*16818Swall int usage(); /* print usage info and exit */ 58*16818Swall int creation(); /* sort jobs by date of creation */ 59*16818Swall int alphasort(); /* sort jobs by date of execution */ 60*16818Swall int filewanted(); /* should a file be included in queue?*/ 61*16818Swall int printqueue(); /* print the queue */ 62*16818Swall int countfiles(); /* count the number of files in queue 63*16818Swall for a given person */ 64*16818Swall char **namelist; /* array of specific name(s) requested*/ 65*16818Swall 66*16818Swall 67*16818Swall --argc, ++argv; 68*16818Swall 69*16818Swall /* 70*16818Swall * Interpret command line flags if they exist. 71*16818Swall */ 72*16818Swall while (argc > 0 && **argv == '-') { 73*16818Swall (*argv)++; 74*16818Swall while (**argv) switch (*(*argv)++) { 75*16818Swall 76*16818Swall case 'c' : cflag++; 77*16818Swall break; 78*16818Swall 79*16818Swall case 'n' : nflag++; 80*16818Swall break; 81*16818Swall 82*16818Swall default : usage(); 83*16818Swall 84*16818Swall } 85*16818Swall --argc, ++argv; 86*16818Swall } 87*16818Swall 88*16818Swall /* 89*16818Swall * If a certain name (or names) is requested, set a pointer to the 90*16818Swall * beginning of the list. 91*16818Swall */ 92*16818Swall if (**argv) { 93*16818Swall ++namewanted; 94*16818Swall namelist = argv; 95*16818Swall } 96*16818Swall 97*16818Swall /* 98*16818Swall * Move to the spooling area and scan the directory, placing the 99*16818Swall * files in the queue structure. The queue comes back sorted by 100*16818Swall * execution time or creation time. 101*16818Swall */ 102*16818Swall if (chdir(ATDIR) == -1) { 103*16818Swall perror(ATDIR); 104*16818Swall exit(1); 105*16818Swall } 106*16818Swall if ((numentries = scandir(".",&queue,filewanted, (cflag) ? creation : 107*16818Swall alphasort)) < 0) { 108*16818Swall perror(ATDIR); 109*16818Swall exit(1); 110*16818Swall } 111*16818Swall 112*16818Swall /* 113*16818Swall * Either print a message stating: 114*16818Swall * 115*16818Swall * 1) that the spooling area is empty. 116*16818Swall * 2) the number of jobs in the spooling area. 117*16818Swall * 3) the number of jobs in the spooling area belonging to 118*16818Swall * a certain person. 119*16818Swall * 4) that the person requested doesn't have any files in the 120*16818Swall * spooling area. 121*16818Swall * 122*16818Swall * or send the queue off to "printqueue" for printing. 123*16818Swall * 124*16818Swall * This whole process might seem a bit elaborate, but it's worthwhile 125*16818Swall * to print some informative messages for the user. 126*16818Swall * 127*16818Swall */ 128*16818Swall if ((numentries == 0) && (!nflag)) { 129*16818Swall printf("no files in queue.\n"); 130*16818Swall exit(0); 131*16818Swall } 132*16818Swall if (nflag) { 133*16818Swall printf("%d\n",(namewanted) ? countfiles(namelist) : numentries); 134*16818Swall exit(0); 135*16818Swall } 136*16818Swall if ((namewanted) && (countfiles(namelist) == 0)) { 137*16818Swall printf("no files for %s.\n", (argc == 1) ? 138*16818Swall *argv : "specified users"); 139*16818Swall exit(0); 140*16818Swall } 141*16818Swall printqueue(namelist); 142*16818Swall exit(0); 143*16818Swall } 144*16818Swall 145*16818Swall /* 146*16818Swall * Count the number of jobs in the spooling area owned by a certain person(s). 147*16818Swall */ 148*16818Swall countfiles(namelist) 149*16818Swall char **namelist; 150*16818Swall { 151*16818Swall int i; /* for loop index */ 152*16818Swall int entryfound; /* found file owned by user(s)*/ 153*16818Swall int numfiles = 0; /* number of files owned by a 154*16818Swall certain person(s) */ 155*16818Swall char **ptr; /* scratch pointer */ 156*16818Swall struct stat stbuf; /* buffer for file stats */ 157*16818Swall 158*16818Swall 159*16818Swall 160*16818Swall /* 161*16818Swall * For each file in the queue, see if the user(s) own the file. We 162*16818Swall * have to use "entryfound" (rather than simply incrementing "numfiles") 163*16818Swall * so that if a person's name appears twice on the command line we 164*16818Swall * don't double the number of files owned by him/her. 165*16818Swall */ 166*16818Swall for (i = 0; i < numentries ; i++) { 167*16818Swall if ((stat(queue[i]->d_name, &stbuf)) < 0) { 168*16818Swall continue; 169*16818Swall } 170*16818Swall ptr = namelist; 171*16818Swall entryfound = 0; 172*16818Swall 173*16818Swall while (*ptr) { 174*16818Swall if (getid(*ptr) == stbuf.st_uid) 175*16818Swall ++entryfound; 176*16818Swall ++ptr; 177*16818Swall } 178*16818Swall if (entryfound) 179*16818Swall ++numfiles; 180*16818Swall } 181*16818Swall return(numfiles); 182*16818Swall } 183*16818Swall 184*16818Swall /* 185*16818Swall * Print the queue. If only jobs belonging to a certain person(s) are requested, 186*16818Swall * only print jobs that belong to that person(s). 187*16818Swall */ 188*16818Swall printqueue(namelist) 189*16818Swall char **namelist; 190*16818Swall { 191*16818Swall int i; /* for loop index */ 192*16818Swall int rank = 1; /* rank of a job */ 193*16818Swall int entryfound; /* found file owned by user(s)*/ 194*16818Swall int printrank(); /* print the rank of a job */ 195*16818Swall int plastrun(); /* print the last time the 196*16818Swall spooling area was updated */ 197*16818Swall int getid(); /* get uid of a person */ 198*16818Swall char **ptr; /* scratch pointer */ 199*16818Swall char *getname(); /* get the login name of a 200*16818Swall person using their uid */ 201*16818Swall struct stat stbuf; /* buffer for file stats */ 202*16818Swall 203*16818Swall 204*16818Swall /* 205*16818Swall * Print the time the spooling area was last modified and the header 206*16818Swall * for the queue. 207*16818Swall */ 208*16818Swall plastrun(); 209*16818Swall printf(" Rank Execution Date Owner Job # Job Name\n"); 210*16818Swall 211*16818Swall /* 212*16818Swall * Print the queue. If a certain name(s) was requested, print only jobs 213*16818Swall * belonging to that person(s), otherwise print the entire queue. 214*16818Swall * Once again, we have to use "entryfound" (rather than simply 215*16818Swall * comparing each command line argument) so that if a person's name 216*16818Swall * appears twice we don't print each file owned by him/her twice. 217*16818Swall * 218*16818Swall * 219*16818Swall * "printrank", "printdate", and "printjobname" all take existing 220*16818Swall * data and display it in a friendly manner. 221*16818Swall * 222*16818Swall */ 223*16818Swall for (i = 0; i < numentries; i++) { 224*16818Swall if ((stat(queue[i]->d_name, &stbuf)) < 0) { 225*16818Swall continue; 226*16818Swall } 227*16818Swall if (namewanted) { 228*16818Swall ptr = namelist; 229*16818Swall entryfound = 0; 230*16818Swall 231*16818Swall while (*ptr) { 232*16818Swall if (getid(*ptr) == stbuf.st_uid) 233*16818Swall ++entryfound; 234*16818Swall ++ptr; 235*16818Swall } 236*16818Swall if (!entryfound) 237*16818Swall continue; 238*16818Swall } 239*16818Swall printrank(rank++); 240*16818Swall printdate(queue[i]->d_name); 241*16818Swall printf("%-10.9s",getname(stbuf.st_uid)); 242*16818Swall printf("%5d",stbuf.st_ino); 243*16818Swall printjobname(queue[i]->d_name); 244*16818Swall } 245*16818Swall ++ptr; 246*16818Swall } 247*16818Swall 248*16818Swall /* 249*16818Swall * Get the full login name of a person using his/her user id. 250*16818Swall */ 251*16818Swall char * 252*16818Swall getname(uid) 253*16818Swall int uid; 254*16818Swall { 255*16818Swall struct passwd *pwdinfo; /* password info structure */ 256*16818Swall 257*16818Swall 258*16818Swall if ((pwdinfo = getpwuid(uid)) == 0) { 259*16818Swall perror(uid); 260*16818Swall exit(1); 261*16818Swall } 262*16818Swall return(pwdinfo->pw_name); 263*16818Swall } 264*16818Swall 265*16818Swall /* 266*16818Swall * Get the uid of a person using his/her login name. Return -1 if no 267*16818Swall * such account name exists. 268*16818Swall */ 269*16818Swall getid(name) 270*16818Swall char *name; 271*16818Swall { 272*16818Swall 273*16818Swall struct passwd *pwdinfo; /* password info structure */ 274*16818Swall 275*16818Swall 276*16818Swall if ((pwdinfo = getpwnam(name)) == 0) 277*16818Swall return(-1); 278*16818Swall 279*16818Swall return(pwdinfo->pw_uid); 280*16818Swall } 281*16818Swall 282*16818Swall /* 283*16818Swall * Print the time the spooling area was updated. 284*16818Swall */ 285*16818Swall plastrun() 286*16818Swall { 287*16818Swall struct timeval now; /* time it is right now */ 288*16818Swall struct timezone zone; /* NOT USED */ 289*16818Swall struct tm *loc; /* detail of time it is right */ 290*16818Swall u_long lasttime; /* last update time in seconds 291*16818Swall since 1/1/70 */ 292*16818Swall FILE *last; /* file where last update hour 293*16818Swall is stored */ 294*16818Swall 295*16818Swall 296*16818Swall /* 297*16818Swall * Open the file where the last update time is stored, and grab the 298*16818Swall * last update hour. The update time is measured in seconds since 299*16818Swall * 1/1/70. 300*16818Swall */ 301*16818Swall if ((last = fopen(LASTFILE,"r")) == NULL) { 302*16818Swall perror(LASTFILE); 303*16818Swall exit(1); 304*16818Swall } 305*16818Swall fscanf(last,"%d",(u_long) &lasttime); 306*16818Swall fclose(last); 307*16818Swall 308*16818Swall /* 309*16818Swall * Get a broken down representation of the last update time. 310*16818Swall */ 311*16818Swall loc = localtime(&lasttime); 312*16818Swall 313*16818Swall /* 314*16818Swall * Print the time that the spooling area was last updated. 315*16818Swall */ 316*16818Swall printf("\n LAST EXECUTION TIME: %s ",mthnames[loc->tm_mon]); 317*16818Swall printf("%d, 19%d ",loc->tm_mday,loc->tm_year); 318*16818Swall printf("at %d:%02d\n\n",loc->tm_hour,loc->tm_min); 319*16818Swall } 320*16818Swall 321*16818Swall /* 322*16818Swall * Print the rank of a job. (I've got to admit it, I stole it from "lpq") 323*16818Swall */ 324*16818Swall static 325*16818Swall printrank(n) 326*16818Swall { 327*16818Swall static char *r[] = { 328*16818Swall "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 329*16818Swall }; 330*16818Swall 331*16818Swall if ((n/10) == 1) 332*16818Swall printf("%3d%-5s", n,"th"); 333*16818Swall else 334*16818Swall printf("%3d%-5s", n, r[n%10]); 335*16818Swall } 336*16818Swall 337*16818Swall /* 338*16818Swall * Print the date that a job is to be executed. This takes some manipulation 339*16818Swall * of the file name. 340*16818Swall */ 341*16818Swall printdate(filename) 342*16818Swall char *filename; 343*16818Swall { 344*16818Swall int yday = 0; /* day of year file will be 345*16818Swall executed */ 346*16818Swall int min = 0; /* min. file will be executed */ 347*16818Swall int hour = 0; /* hour file will be executed */ 348*16818Swall int day = 0; /* day file will be executed */ 349*16818Swall int month = 0; /* month file will be executed*/ 350*16818Swall int year = 0; /* year file will be executed */ 351*16818Swall int get_mth_day(); /* convert a day of year to a 352*16818Swall month and day of month */ 353*16818Swall char date[18]; /* reformatted execution date */ 354*16818Swall 355*16818Swall /* 356*16818Swall * Pick off the necessary info from the file name and convert the day 357*16818Swall * of year to a month and day of month. 358*16818Swall */ 359*16818Swall sscanf(filename,"%2d.%3d.%2d%2d",&year,&yday,&hour,&min); 360*16818Swall get_mth_day(year,yday,&month,&day); 361*16818Swall 362*16818Swall /* 363*16818Swall * Format the execution date of a job. 364*16818Swall */ 365*16818Swall sprintf(date,"%3s %2d, 19%2d %02d:%02d",mthnames[month], 366*16818Swall day, year,hour,min); 367*16818Swall 368*16818Swall /* 369*16818Swall * Print the date the job will be executed. 370*16818Swall */ 371*16818Swall printf("%-21.18s",date); 372*16818Swall } 373*16818Swall 374*16818Swall /* 375*16818Swall * Given a day of the year, calculate the month and day of month. 376*16818Swall */ 377*16818Swall get_mth_day(year,dayofyear,month,day) 378*16818Swall int year, dayofyear, *month, *day; 379*16818Swall 380*16818Swall { 381*16818Swall 382*16818Swall int i = 1; /* for loop index */ 383*16818Swall int leap; /* are we dealing with a leap 384*16818Swall year? */ 385*16818Swall /* Table of the number of days 386*16818Swall in each month of the year. 387*16818Swall 388*16818Swall dofy_tab[1] -- regular year 389*16818Swall dofy_tab[2] -- leap year 390*16818Swall */ 391*16818Swall 392*16818Swall static int dofy_tab[2][13] = { 393*16818Swall { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 394*16818Swall { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 395*16818Swall }; 396*16818Swall 397*16818Swall /* 398*16818Swall * Are we dealing with a leap year? 399*16818Swall */ 400*16818Swall leap = ((year%4 == 0 && year%100 != 0) || year%100 == 0); 401*16818Swall 402*16818Swall /* 403*16818Swall * Calculate the month of the year and day of the month. 404*16818Swall */ 405*16818Swall while (dayofyear >= dofy_tab[leap][i]) { 406*16818Swall dayofyear -= dofy_tab[leap][i++]; 407*16818Swall ++(*month); 408*16818Swall } 409*16818Swall *day = (dayofyear + 1); 410*16818Swall } 411*16818Swall 412*16818Swall /* 413*16818Swall * Print a job name. If the old "at" has been used to create the spoolfile, 414*16818Swall * the three line header that the new version of "at" puts in the spoolfile. 415*16818Swall * Thus, we just print "???". 416*16818Swall */ 417*16818Swall printjobname(file) 418*16818Swall char *file; 419*16818Swall { 420*16818Swall char *ch; /* char ptr */ 421*16818Swall char jobname[80]; /* the job name */ 422*16818Swall FILE *filename; /* job file in spooling area */ 423*16818Swall 424*16818Swall /* 425*16818Swall * Open the job file and grab the first line. 426*16818Swall */ 427*16818Swall printf(" "); 428*16818Swall 429*16818Swall if ((filename = fopen(file,"r")) == NULL) { 430*16818Swall printf("%.27s\n", "???"); 431*16818Swall fclose(filename); 432*16818Swall return; 433*16818Swall } 434*16818Swall if (fscanf(filename,"# jobname: %s",jobname) != 1) { 435*16818Swall printf("%.27s\n", "???"); 436*16818Swall fclose(filename); 437*16818Swall return; 438*16818Swall } 439*16818Swall fclose(filename); 440*16818Swall 441*16818Swall /* 442*16818Swall * Put a pointer at the begining of the line and remove the basename 443*16818Swall * from the job file. 444*16818Swall */ 445*16818Swall ch = jobname; 446*16818Swall if ((ch = (char *)rindex(jobname,'/')) != 0) 447*16818Swall ++ch; 448*16818Swall else 449*16818Swall ch = jobname; 450*16818Swall 451*16818Swall printf("%.27s\n",ch); 452*16818Swall } 453*16818Swall 454*16818Swall /* 455*16818Swall * Do we want to include a file in the queue? (used by "scandir") We are looking 456*16818Swall * for files with following syntax: yy.ddd.hhhh. so the test is made to see if 457*16818Swall * the file name has three dots in it. This test will suffice since the only 458*16818Swall * other files in /usr/spool/at don't have any dots in their name. 459*16818Swall */ 460*16818Swall filewanted(direntry) 461*16818Swall struct direct *direntry; 462*16818Swall { 463*16818Swall int numdot = 0; 464*16818Swall char *filename; 465*16818Swall 466*16818Swall filename = direntry->d_name; 467*16818Swall while (*filename) 468*16818Swall numdot += (*(filename++) == '.'); 469*16818Swall return(numdot == 3); 470*16818Swall } 471*16818Swall 472*16818Swall /* 473*16818Swall * Sort files by time of creation. (used by "scandir") 474*16818Swall */ 475*16818Swall creation(d1, d2) 476*16818Swall struct direct **d1, **d2; 477*16818Swall { 478*16818Swall struct stat stbuf1, stbuf2; 479*16818Swall 480*16818Swall if (stat((*d1)->d_name,&stbuf1) < 0) 481*16818Swall return(1); 482*16818Swall 483*16818Swall if (stat((*d2)->d_name,&stbuf2) < 0) 484*16818Swall return(1); 485*16818Swall 486*16818Swall return(stbuf1.st_ctime < stbuf2.st_ctime); 487*16818Swall } 488*16818Swall 489*16818Swall /* 490*16818Swall * Print usage info and exit. 491*16818Swall */ 492*16818Swall usage() 493*16818Swall { 494*16818Swall fprintf(stderr,"usage: atq [-c] [-n] [name ...]\n"); 495*16818Swall exit(1); 496*16818Swall } 497