This is a patch for "at" 3.1.11. This patch modifies the "atd" daemon. The modified version cancels jobs that can't be executed (for any reason) within about CHECK_ INTERVAL seconds after the scheduled time. To restore the original behavior, #define the symbol RESCHEDULE_JOBS. --- at-3.1.11.old/atd.c +++ at-3.1.11/atd.c @@ -503,6 +503,167 @@ exit(EXIT_SUCCESS); } +//-------------------------------------------------------------------- + +// By default, this version of "atd" cancels jobs that can't be execu- +// ted [for any reason] within about CHECK_INTERVAL seconds after the +// scheduled time. To restore the original behavior, #define the sym- +// bol RESCHEDULE_JOBS. + +// "run_loop" calls NotifyCancel to cancel an expired job. This rou- +// tine extracts basic job parameters from a specified job file, de- +// letes the file, and sends an E-mail message to the user who sched- +// uled the job. + +// NotifyCancel spawns a subprocess to send the message. If an error +// occurs [either before the message is sent or during transmission], +// the original NotifyCancel call simply returns. The subprocess sim- +// ply exits. Possible future change: NotifyCancel should log err- +// ors. + +// For more information, see "run_loop". + +//-------------------------------------------------------------------- + +#ifndef RESCHEDULE_JOBS // If jobs _aren't_ rescheduled +static void NotifyCancel (char *filename) +{ + int n; // Scratch + + FILE *ifp; // Input stream pointer + FILE *ofp; // Output stream pointer + + char fmt [128]; // "sscanf" format-string buffer + char xfname [128]; // Temporary-file name buffer + + unsigned long jobno; // Job number [as an integer] + char jobbuf [17]; // Job number [as a string ] + + // Pointer to account-name buffer + static char *mailname = NULL; + // Account-name size parameter + static int mailsize = 128; + +//-------------------------------------------------------------------- + +// The following three variables are used only as "sscanf" placeholder +// buffers. + + char queue; // Queue-type identifier + int nuid; // User ID number + int ngid; // Group ID number + +//-------------------------------------------------------------------- +// Set up static variables. + + if (mailname == NULL) // Are mailname/mailsize ready? + { // No - Initialize them now +#ifdef _SC_LOGIN_NAME_MAX + int rc = sysconf (_SC_LOGIN_NAME_MAX); + if (rc > 0) mailsize = rc; +#else +#ifdef LOGIN_NAME_MAX + mailsize = LOGIN_NAME_MAX; +#endif +#endif + if ((mailname = malloc (mailsize + 1)) == NULL) return; + } + +//-------------------------------------------------------------------- +// Additional setup. + + // Create "sscanf" format string + sprintf (fmt, + "#!/bin/sh\n# atrun uid=%%d gid=%%d\n# mail %%%ds", + mailsize); + // Open job file for reading +PRIV_START + ifp = fopen (filename, "r"); +PRIV_END + if (ifp == NULL) return; + // Scan the job file + n = fscanf (ifp, fmt, &nuid, &ngid, mailname); + fclose (ifp); // Close the job file + + if (n != 3) return; // Consistency check + // Ditto + if (mailname [0] == '-') return; + // Get the job number + n = sscanf (filename, "%c%5lx", &queue, &jobno); + + if (n != 2) return; // Consistency check + // Create "string" version + sprintf (jobbuf, "%lu", jobno); + // Delete the job file + if (unlink (filename)) return; + + if (fork()) // Start a subprocess + { // This is the parent + return; // We're done at this level + } + +//-------------------------------------------------------------------- +// Create an appropriate E-mail message. + + // Set temporary-file name + sprintf (xfname, + "/tmp/temp-mail-%d-%d.delete", getuid(), getpid()); + + // Open temporary file for writing +PRIV_START + ofp = fopen (xfname, "w"); +PRIV_END + if (ofp == NULL) exit (1); + // Write E-mail text to file + fprintf (ofp, + "Subject: Job number %s was cancelled\n", jobbuf); + + fprintf (ofp, "To: %s\n\n", mailname); + fprintf (ofp, +"Your previously-scheduled job [number %s] couldn't", jobbuf); + + fprintf (ofp, " be executed.\n"); + fprintf (ofp, +"The system may have been down or undergoing maintenance at the\n"); + + fprintf (ofp, "scheduled time.\n"); + fprintf (ofp, +"\nNote: This message was sent by the job scheduler.\n"); + + fclose (ofp); // Close the temporary file + +//-------------------------------------------------------------------- +// Wrap it up. + + // Open temporary file for reading +PRIV_START + ifp = fopen (xfname, "r"); +PRIV_END + if (ifp == NULL) exit (1); + + unlink (xfname); // Pre-delete the file + // Attach "stdin" to the stream + close (STDIN_FILENO); + if (dup (fileno (ifp)) != STDIN_FILENO) exit (1); + + fclose (ifp); // Close the original stream + +PRIV_START + chdir ("/"); // Just in case + // E-mail the message +#ifdef SENDMAIL + execl (SENDMAIL, "sendmail", mailname, (char *) NULL); +#else +#error "No mail command specified" +#endif +PRIV_END + + exit (1); // Error exit +} +#endif // Endif RESCHEDULE_JOBS + +//-------------------------------------------------------------------- + static time_t run_loop() { @@ -594,6 +755,13 @@ if (!(isupper(queue) || islower(queue))) { continue; } + +// By default, this version of "atd" cancels jobs that can't be execu- +// ted [for any reason] within about CHECK_INTERVAL seconds after the +// scheduled time. To restore the original behavior, #define the sym- +// bol RESCHEDULE_JOBS. + +#ifdef RESCHEDULE_JOBS // Old behavior /* Is the file already locked? */ if (buf.st_nlink > 1) { @@ -610,6 +778,18 @@ } continue; } +#else // Cancel jobs that don't run + if ((run_time + CHECK_INTERVAL) <= now) + { + // Delete lock file [if it exists] + strncpy (lock_name, dirent->d_name, sizeof (lock_name)); + lock_name [0] = '='; + unlink (lock_name); + // Delete job file and notify user + NotifyCancel (dirent->d_name); + continue; + } +#endif // Endif RESCHEDULE_JOBS /* If we got here, then there are jobs of some kind waiting. * We could try to be smarter and leave nothing_to_do set if