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