(void) strcpy (User, optarg);
(void) strncpy (User, optarg, MAX_UNAME - 1);
  edit_cmd __P ((void)),  poke_daemon __P ((void)),  check_error __P ((char *)),  parse_args __P ((int c, char *v[], char **const f));static int replace_cmd __P ((void));static voidusage (char *msg){  fprintf (stderr, "%s: usage error: %s\n", ProgramName, msg);  fprintf (stderr, "usage:\t%s [-u user] file\n", ProgramName);  fprintf (stderr, "\t%s [-u user] { -e | -l | -r }\n", ProgramName);  fprintf (stderr, "\t\t(default operation is replace, per 1003.2)\n");  fprintf (stderr, "\t-e\t(edit user's crontab)\n");  fprintf (stderr, "\t-l\t(list user's crontab)\n");  fprintf (stderr, "\t-r\t(delete user's crontab)\n");  exit (ERROR_EXIT);}intmain (int argc, char *argv[]){  int exitstatus;  char *config_file;		/* Name of our configuration file;  NULL if none */  Pid = getpid ();  ProgramName = argv[0];#if defined(POSIX)  setlocale (LC_ALL, "");#endif#if HAVE_SETLINEBUF  setlinebuf (stderr);#endif /*HAVE_SETLINEBUF */  parse_args (argc, argv, &config_file);	/* sets many globals, opens a file */  read_config (config_file, &allow_only_root, &log_syslog, &allow_file,	       &deny_file, &crondir, &spool_dir, &log_file, &syscrontab,	       &lastrun_file, &pidfile, &mailprog, &mailargs);  set_cron_cwd ();  if (!allowed (User))    {      fprintf (stderr,	       "You (%s) are not allowed to use this program (%s)\n",	       User, ProgramName);      fprintf (stderr, "See crontab(1) for more information\n");      log_it (RealUser, Pid, "AUTH", "crontab command not allowed");      exit (ERROR_EXIT);    }  exitstatus = OK_EXIT;  switch (Option)    {    case opt_list:      list_cmd ();      break;    case opt_delete:      delete_cmd ();      break;    case opt_edit:      edit_cmd ();      break;    case opt_replace:      if (replace_cmd () < 0)	exitstatus = ERROR_EXIT;      break;    }  exit (0); /*NOTREACHED*/}static voidparse_args (int argc, char *argv[], char **const config_file_p){  int argch;#ifdef CONFIG_FILE  *config_file_p = CONFIG_FILE;#else  *config_file_p = NULL;#endif  if (!(pw = getpwuid (getuid ())))    {      fprintf (stderr, "%s: your UID isn't in the passwd file.\n",	       ProgramName);      fprintf (stderr, "bailing out.\n");      exit (ERROR_EXIT);    }  strcpy (User, pw->pw_name);  strcpy (RealUser, User);  Filename[0] = '\0';  Option = opt_unknown;  while (EOF != (argch = getopt (argc, argv, "u:lerx:")))    {      switch (argch)	{	case 'x':	  if (!set_debug_flags (optarg))	    usage ("bad debug option");	  break;	case 'u':	  if (getuid () != ROOT_UID)	    {	      fprintf (stderr, "must be privileged to use -u\n");	      exit (ERROR_EXIT);	    }	  if (!(pw = getpwnam (optarg)))	    {	      fprintf (stderr, "%s:  user `%s' unknown\n",		       ProgramName, optarg);	      exit (ERROR_EXIT);	    }	  (void) strcpy (User, optarg);	  break;	case 'l':	  if (Option != opt_unknown)	    usage ("only one operation permitted");	  Option = opt_list;	  break;	case 'r':	  if (Option != opt_unknown)	    usage ("only one operation permitted");	  Option = opt_delete;	  break;	case 'e':	  if (Option != opt_unknown)	    usage ("only one operation permitted");	  Option = opt_edit;	  break;	default:	  usage ("unrecognized option");	}    }  endpwent ();  if (Option != opt_unknown)    {      if (argv[optind] != NULL)	{	  usage ("no arguments permitted after this option");	}    }  else    {      if (argv[optind] != NULL)	{	  Option = opt_replace;	  (void) strncpy (Filename, argv[optind], sizeof (Filename) - 1);	  Filename[sizeof (Filename) - 1] = '\0';	}      else	{	  usage ("file name must be specified for replace");	}    }  if (Option == opt_replace)    {      /* we have to open the file here because we're going to       * chdir(2) into /var/cron before we get around to       * reading the file.       */      if (!strcmp (Filename, "-"))	{	  NewCrontab = stdin;	}      else	{	  /* relinquish the setuid status of the binary during	   * the open, lest nonroot users read files they should	   * not be able to read.  we can't use access() here	   * since there's a race condition.  thanks go out to	   * Arnt Gulbrandsen <agulbra@pvv.unit.no> for spotting	   * the race.	   */	  if (swap_uids () < OK)	    {	      perror ("swapping uids");	      exit (ERROR_EXIT);	    }	  if (!(NewCrontab = fopen (Filename, "r")))	    {	      perror (Filename);	      exit (ERROR_EXIT);	    }	  if (swap_uids () < OK)	    {	      perror ("swapping uids back");	      exit (ERROR_EXIT);	    }	}    }  Debug (DMISC, ("user=%s, file=%s, option=%s\n",		 User, Filename, Options[(int) Option]));}static voidlist_cmd (void){  char n[MAX_FNAME];  FILE *f;  int ch;  log_it (RealUser, Pid, "LIST", User);  (void) snprintf (n, MAX_FNAME, CRON_TAB (User));  if (!(f = fopen (n, "r")))    {      if (errno == ENOENT)	fprintf (stderr, "no crontab for %s\n", User);      else	perror (n);      exit (ERROR_EXIT);    }  /* file is open. copy to stdout, close.   */  Set_LineNum (1);  while (EOF != (ch = get_char (f)))    putchar (ch);  fclose (f);}static voiddelete_cmd (void){  char n[MAX_FNAME];  log_it (RealUser, Pid, "DELETE", User);  (void) snprintf (n, MAX_FNAME, CRON_TAB (User));  if (unlink (n))    {      if (errno == ENOENT)	fprintf (stderr, "no crontab for %s\n", User);      else	perror (n);      exit (ERROR_EXIT);    }  poke_daemon ();}static voidcheck_error (char *msg){  CheckErrorCount++;  fprintf (stderr, "\"%s\":%d: %s\n", Filename, LineNumber - 1, msg);}static voidremove_editor_file (const char Filename[]){  /* Delete the temporary file we created for the user to edit.  Because     we made the user the owner, we may have to swap uids now to have the     permission to unlink it (if we are setuid cron).   */  int rc;  if (swap_uids () < OK)    {      perror ("swapping uids");      exit (ERROR_EXIT);    }  rc = unlink (Filename);  if (rc < 0)    fprintf (stderr, "Warning: unable to delete temporary file %s.\n"	     "errno = %s (%d).\n", Filename, strerror (errno), errno);  if (swap_uids () < OK)    {      perror ("swapping uids back");      exit (ERROR_EXIT);    }}static voidedit_cmd (void){  char n[MAX_FNAME], q[MAX_TEMPSTR], *editor;  FILE *f;  int t;  struct stat statbuf;  time_t mtime;  WAIT_T waiter;  pid_t pid, xpid;  log_it (RealUser, Pid, "BEGIN EDIT", User);  (void) snprintf (n, MAX_FNAME, CRON_TAB (User));  if (!(f = fopen (n, "r")))    {      if (errno != ENOENT)	{	  perror (n);	  exit (ERROR_EXIT);	}      fprintf (stderr, "no crontab for %s - using an empty one\n", User);      if (!(f = fopen ("/dev/null", "r")))	{	  perror ("/dev/null");	  exit (ERROR_EXIT);	}    }  /* swap uids for the open so that the temporary file we create to     edit will be properly owned so that our unprivileged child can     edit it.     */  if (swap_uids () < OK)    {      perror ("swapping uids");      exit (ERROR_EXIT);    }  (void) snprintf (Filename, MAX_FNAME, "/tmp/crontab.%d", Pid);  if (-1 == (t = open (Filename, O_CREAT | O_EXCL | O_RDWR, 0600)))    {      perror (Filename);      goto fatal;    }  if (swap_uids () < OK)    {      perror ("swapping uids back");      exit (ERROR_EXIT);    }  if (!(NewCrontab = fdopen (t, "r+")))    {      perror ("fdopen");      goto fatal;    }  while (!feof (f) && !ferror (f))    {      char line[80 + 1];      char *retval;      retval = fgets (line, sizeof (line), f);      if (retval != NULL)	{	  if (strncmp (line, "#CRONTAB", sizeof ("#CRONTAB") - 1) == 0)	    {	      /* We have a comment generated by this program, so ignore 	         the entire line.	       */	      while (!feof (f) && !ferror (f)		     && line[strlen (line) - 1] != '\n')		fgets (line, sizeof (line), f);	    }	  else	    {	      /* It's a regular line; copy it over */	      fputs (line, NewCrontab);	    }	}    }  fclose (f);  if (fflush (NewCrontab) < OK)    {      perror (Filename);      exit (ERROR_EXIT);    }again:  rewind (NewCrontab);  if (ferror (NewCrontab))    {      fprintf (stderr, "%s: error while writing new crontab to %s\n",	       ProgramName, Filename);    fatal:remove_editor_file (Filename);      exit (ERROR_EXIT);    }  if (fstat (t, &statbuf) < 0)    {      perror ("fstat");      goto fatal;    }  mtime = statbuf.st_mtime;  if ((!(editor = getenv ("VISUAL"))) && (!(editor = getenv ("EDITOR"))))    {      editor = EDITOR;    }  /* we still have the file open.  editors will generally rewrite the   * original file rather than renaming/unlinking it and starting a   * new one; even backup files are supposed to be made by copying   * rather than by renaming.  if some editor does not support this,   * then don't use it.  the security problems are more severe if we   * close and reopen the file around the edit.   */  switch (pid = fork ())    {    case -1:      perror ("fork");      goto fatal;    case 0:      /* child */      if (setuid (getuid ()) < 0)	{	  perror ("setuid(getuid())");	  exit (ERROR_EXIT);	}      if (chdir ("/tmp") < 0)	{	  perror ("chdir(/tmp)");	  exit (ERROR_EXIT);	}      if (strlen (editor) + strlen (Filename) + 2 >= MAX_TEMPSTR)	{	  fprintf (stderr, "%s: editor or filename too long\n", ProgramName);	  exit (ERROR_EXIT);	}      snprintf (q, MAX_TEMPSTR, "%s %s", editor, Filename);      execlp (_PATH_BSHELL, _PATH_BSHELL, "-c", q, NULL);      perror (editor);      exit (ERROR_EXIT);     /*NOTREACHED*/ default:      /* parent */      break;    }  /* parent */  xpid = wait (&waiter);  if (xpid != pid)    {      fprintf (stderr, "%s: wrong PID (%d != %d) from \"%s\"\n",	       ProgramName, xpid, pid, editor);      goto fatal;    }  if (WIFEXITED (waiter) && WEXITSTATUS (waiter))    {      fprintf (stderr, "%s: \"%s\" exited with status %d\n",	       ProgramName, editor, WEXITSTATUS (waiter));      goto fatal;    }  if (WIFSIGNALED (waiter))    {      fprintf (stderr,	       "%s: \"%s\" killed; signal %d (%score dumped)\n",	       ProgramName, editor, WTERMSIG (waiter),	       WCOREDUMP (waiter) ? "" : "no ");      goto fatal;    }  if (fstat (t, &statbuf) < 0)    {      perror ("fstat");      goto fatal;    }  if (mtime == statbuf.st_mtime)    {      fprintf (stderr, "%s: no changes made to crontab\n", ProgramName);    }  else    {      fprintf (stderr, "%s: installing new crontab\n", ProgramName);      switch (replace_cmd ())	{	case 0:	  break;	case -1:	  for (;;)	    {	      printf ("Do you want to retry the same edit? ");	      fflush (stdout);	      q[0] = '\0';	      (void) fgets (q, sizeof q, stdin);	      switch (islower (q[0]) ? q[0] : tolower (q[0]))		{		case 'y':		  goto again;		case 'n':		  goto abandon;		default:		  fprintf (stderr, "Enter Y or N\n");		}	    }	 /*NOTREACHED*/ case -2:	abandon:	  fprintf (stderr, "%s: edits left in %s\n", ProgramName, Filename);	  goto done;	default:	  fprintf (stderr, "%s: panic: bad switch() in replace_cmd()\n",		   ProgramName);	  goto fatal;	}    }  remove_editor_file (Filename);done:  log_it (RealUser, Pid, "END EDIT", User);}