Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 8 Jun 2002 17:52:07 -0400
From:      Garance A Drosihn <drosih@rpi.edu>
To:        freebsd-audit@freebsd.org
Cc:        freebsd-print@bostonradio.org
Subject:   Rewrite of much of lpc/cmds.c ...
Message-ID:  <p05111731b928291892ef@[128.113.24.47]>

next in thread | raw e-mail | index | archive | help
So, as I compare freebsd's lpc to RPI's lpc, I notice two things
about many of lpc's commands:

    1) a lot of commands duplicate the code to modify the
       state of a queue.
    2) many of them have 'euid' (the priv uid) in effect
       when they write out any error messages.  this seems
       like a bad idea.

That sounds like a tiny thread, but as I pull on it I ended
up rewriting more and more of lpc/cmds.c.  I figure I better
stop at this point, before I rewrite the entire thing.  Here
is an update which basically rewrites

      abort    disable  enable  restart
      start    stop     up

It is also available at:
http://people.freebsd.org/~gad/lpr/lpc-cmds.diff

This just adds the new implementations. The previous versions
remain available as "x"-ed commands (eg:  xabort, xdisable, etc).
This way, if you think there is something odd about the new
implementation, you can resort to the previous implementation
and see if it's really any different.  A later update will
remove the "x"-ed versions.

 From a user standpoint, this is meant to be an almost invisible
change, except that some error situations give a better error
message (IMO).  One other visible change is that the 'restart'
command will only print the printer's name once, instead of
printing it once for the 'abort' part, and a second time for
the 'restart' part.  That had always annoyed me...

I would like to commit this to current around June 15th.  The
two main workhorse routines are set_qstate and kill_qtask.
Since this does not delete anything, you'd have to look at
the original lpc source to see what the old code did.

Here is the update:

Index: common_source/common.c
===================================================================
RCS file: /home/ncvs/src/usr.sbin/lpr/common_source/common.c,v
retrieving revision 1.25
diff -u -r1.25 common.c
--- common_source/common.c	28 May 2002 19:21:10 -0000	1.25
+++ common_source/common.c	8 Jun 2002 21:16:56 -0000
@@ -50,6 +50,7 @@
  #include <sys/types.h>

  #include <dirent.h>
+#include <errno.h>
  #include <fcntl.h>
  #include <stdio.h>
  #include <stdlib.h>
@@ -250,6 +251,134 @@
  		snprintf(buf, len, "%s/%s", pp->spool_dir, pp->status_file);

  	return buf;
+}
+
+/*
+ * Routine to change operational state of a print queue.  The operational
+ * state is indicated by the access bits on the lock file for the queue.
+ * At present, this is only called from various routines in lpc/cmds.c.
+ *
+ *   XXX - Note that this works by changing access-bits on the
+ *         file, and you can only do that if you are the owner
+ *         of the file, or root.  Thus, this won't really work
+ *	   for userids in the "LPR_OPER" group, unless lpc is
+ *	   running setuid to root or maybe setuid to daemon.
+ *	   By default, lpc is installed setgid to daemon, but
+ *	   does not run setuid.
+ */
+int
+set_qstate(int action, const char *lfname)
+{
+	struct stat stbuf;
+	mode_t chgbits, newbits, oldmask;
+	const char *failmsg, *okmsg;
+	int chres, errsav, fd, res, statres;
+
+	/*
+	 * Find what the current access-bits are.
+	 */
+	memset(&stbuf, 0, sizeof(stbuf));
+	seteuid(euid);
+	statres = stat(lfname, &stbuf);
+	errsav = errno;
+	seteuid(uid);
+	if ((statres < 0) && (errsav != ENOENT)) {
+		printf("\tcannot stat() lock file\n");
+		return (SQS_STATFAIL);
+		/* NOTREACHED */
+	}
+
+	/*
+	 * Determine which bit(s) should change for the requested action.
+	 */
+	chgbits = stbuf.st_mode;
+	newbits = LOCK_FILE_MODE;
+	okmsg = NULL;
+	failmsg = NULL;
+	if (action & SQS_DISABLEQ) {
+		chgbits |= LFM_QUEUE_DIS;
+		newbits |= LFM_QUEUE_DIS;
+		okmsg = "queuing disabled";
+		failmsg = "disable queuing";
+	}
+	if (action & SQS_STOPP) {
+		chgbits |= LFM_PRINT_DIS;
+		newbits |= LFM_PRINT_DIS;
+		okmsg = "printing disabled";
+		failmsg = "disable printing";
+	}
+	if (action & SQS_ENABLEQ) {
+		chgbits &= ~LFM_QUEUE_DIS;
+		newbits &= ~LFM_QUEUE_DIS;
+		okmsg = "queuing enabled";
+		failmsg = "enable queuing";
+	}
+	if (action & SQS_STARTP) {
+		chgbits &= ~LFM_PRINT_DIS;
+		newbits &= ~LFM_PRINT_DIS;
+		okmsg = "printing enabled";
+		failmsg = "enable printing";
+	}
+	if (okmsg == NULL) {
+		/* This routine was called with an invalid action. */
+		printf("\t<error in set_qstate!>\n");
+		return (SQS_PARMERR);
+		/* NOTREACHED */
+	}
+
+	res = 0;
+	if (statres >= 0) {
+		/* The file already exists, so change the access. */
+		seteuid(euid);
+		chres = chmod(lfname, chgbits);
+		errsav = errno;
+		seteuid(uid);
+		res = SQS_CHGOK;
+		if (res < 0)
+			res = SQS_CHGFAIL;
+	} else if (newbits == LOCK_FILE_MODE) {
+		/*
+		 * The file does not exist, but the state requested is
+		 * the same as the default state when no file exists.
+		 * Thus, there is no need to create the file.
+		 */
+		res = SQS_SKIPCREOK;
+	} else {
+		/*
+		 * The file did not exist, so create it with the
+		 * appropriate access bits for the requested action.
+		 * Push a new umask around that create, to make sure
+		 * all the read/write bits are set as desired.
+		 */
+		oldmask = umask(S_IWOTH);
+		seteuid(euid);
+		fd = open(lfname, O_WRONLY|O_CREAT, newbits);
+		errsav = errno;
+		seteuid(uid);
+		umask(oldmask);
+		res = SQS_CREFAIL;
+		if (fd >= 0) {
+			res = SQS_CREOK;
+			close(fd);
+		}
+	}
+
+	switch (res) {
+	case SQS_CHGOK:
+	case SQS_CREOK:
+	case SQS_SKIPCREOK:
+		printf("\t%s\n", okmsg);
+		break;
+	case SQS_CREFAIL:
+		printf("\tcannot create lock file: %s\n",
+		    strerror(errsav));
+		break;
+	default:
+		printf("\tcannot %s: %s\n", failmsg, strerror(errsav));
+		break;
+	}
+
+	return (res);
  }

  /* routine to get a current timestamp, optionally in a standard-fmt string */
Index: common_source/lp.h
===================================================================
RCS file: /home/ncvs/src/usr.sbin/lpr/common_source/lp.h,v
retrieving revision 1.20
diff -u -r1.20 lp.h
--- common_source/lp.h	23 Apr 2002 00:06:10 -0000	1.20
+++ common_source/lp.h	8 Jun 2002 21:16:56 -0000
@@ -223,6 +223,23 @@
  #define	TEMP_FILE_MODE	(S_IRUSR | S_IWUSR | S_IRGRP | 
S_IWGRP | S_IROTH)

  /*
+ * Bit-flags for set_qstate() actions, followed by the return values.
+ */
+#define SQS_DISABLEQ	0x01	/* Disable the queuing of new jobs */
+#define SQS_STOPP	0x02	/* Stop the printing of jobs */
+#define SQS_ENABLEQ	0x10	/* Enable the queuing of new jobs */
+#define SQS_STARTP	0x20	/* Start the printing of jobs */
+
+#define SQS_PARMERR	-9	/* Invalid parameters from caller */
+#define SQS_CREFAIL	-3	/* File did not exist, and create failed */
+#define SQS_CHGFAIL	-2	/* File exists, but unable to change state */
+#define SQS_STATFAIL	-1	/* Unable to stat() the lock file */
+#define SQS_CHGOK	1	/* File existed, and the state was changed */
+#define SQS_CREOK	2	/* File did not exist, but was created OK */
+#define SQS_SKIPCREOK	3	/* File did not exist, and there was */
+				/* no need to create it */
+
+/*
   * Command codes used in the protocol.
   */
  #define	CMD_CHECK_QUE	'\1'
@@ -272,6 +289,7 @@
  void	 rmjob(const char *_printer);
  void	 rmremote(const struct printer *_pp);
  void	 setprintcap(char *_newfile);
+int	 set_qstate(int _action, const char *_lfname);
  void	 show(const char *_nfile, const char *_datafile, int _copies);
  int	 startdaemon(const struct printer *_pp);
  char	*status_file_name(const struct printer *_pp, char *_buf,
Index: lpc/cmds.c
===================================================================
RCS file: /home/ncvs/src/usr.sbin/lpr/lpc/cmds.c,v
retrieving revision 1.24
diff -u -r1.24 cmds.c
--- lpc/cmds.c	22 Apr 2002 23:28:42 -0000	1.24
+++ lpc/cmds.c	8 Jun 2002 21:16:56 -0000
@@ -70,9 +70,18 @@
  #include "extern.h"
  #include "pathnames.h"

+/*
+ * Return values from kill_qtask().
+ */
+#define KQT_LFERROR	-2
+#define KQT_KILLFAIL	-1
+#define KQT_NODAEMON	0
+#define KQT_KILLOK	1
+
  static void	 abortpr(struct printer *_pp, int _dis);
  static int	 doarg(char *_job);
  static int	 doselect(struct dirent *_d);
+static int	 kill_qtask(const char *lf);
  static void	 putmsg(struct printer *_pp, int _argc, char **_argv);
  static int	 sortq(const void *_a, const void *_b);
  static void	 startpr(struct printer *_pp, int _chgenable);
@@ -278,6 +287,140 @@
  }

  /*
+ * kill an existing daemon and disable printing.
+ */
+void
+abort_q(struct printer *pp)
+{
+	int killres, setres;
+	char lf[MAXPATHLEN];
+
+	lock_file_name(pp, lf, sizeof lf);
+	printf("%s:\n", pp->printer);
+
+	/*
+	 * Turn on the owner execute bit of the lock file to disable printing.
+	 */
+	setres = set_qstate(SQS_STOPP, lf);
+
+	/*
+	 * If set_qstate found that there already was a lock file, then
+	 * call a routine which will read that lock file and kill the
+	 * lpd-process which is listed in that lock file.  If the lock
+	 * file did not exist, then either there is no daemon running
+	 * for this queue, or there is one running but *it* could not
+	 * write a lock file (which means we can not determine the
+	 * process id of that lpd-process).
+	 */
+	switch (setres) {
+	case SQS_CHGOK:
+	case SQS_CHGFAIL:
+		/* Kill the process */
+		killres = kill_qtask(lf);
+		break;
+	case SQS_CREOK:
+	case SQS_CREFAIL:
+		printf("\tno daemon to abort\n");
+		break;
+	case SQS_STATFAIL:
+		printf("\tassuming no daemon to abort\n");
+		break;
+	default:
+		printf("\t<unexpected result (%d) from set_qstate>\n",
+		    setres);
+		break;
+	}
+
+}
+
+/*
+ * Kill the current daemon, to stop printing of the active job.
+ */
+static int
+kill_qtask(const char *lf)
+{
+	FILE *fp;
+	pid_t pid;
+	int errsav, killres, lockres, res;
+
+	seteuid(euid);
+	fp = fopen(lf, "r");
+	errsav = errno;
+	seteuid(uid);
+	res = KQT_NODAEMON;
+	if (fp == NULL) {
+		/*
+		 * If there is no lock file, then there is no daemon to
+		 * kill.  Any other error return means there is some
+		 * kind of problem with the lock file.
+		 */
+		if (errsav != ENOENT)
+			res = KQT_LFERROR;
+		goto killdone;
+	}
+
+	/* If the lock file is empty, then there is no daemon to kill */
+	if (getline(fp) == 0)
+		goto killdone;
+
+	/*
+	 * If the file can be locked without blocking, then there
+	 * no daemon to kill, or we should not try to kill it.
+	 *
+	 * XXX - not sure I understand the reasoning behind this...
+	 */
+	lockres = flock(fileno(fp), LOCK_SH|LOCK_NB);
+	(void) fclose(fp);
+	if (lockres == 0)
+		goto killdone;
+
+	pid = atoi(line);
+	if (pid < 0) {
+		/*
+		 * If we got a negative pid, then the contents of the
+		 * lock file is not valid.
+		 */
+		res = KQT_LFERROR;
+		goto killdone;
+	}
+
+	seteuid(uid);
+	killres = kill(pid, SIGTERM);
+	errsav = errno;
+	seteuid(uid);
+	if (killres == 0) {
+		res = KQT_KILLOK;
+		printf("\tdaemon (pid %d) killed\n", pid);
+	} else if (errno == ESRCH) {
+		res = KQT_NODAEMON;
+	} else {
+		res = KQT_KILLFAIL;
+		printf("\tWarning: daemon (pid %d) not killed:\n", pid);
+		printf("\t    %s\n", strerror(errsav));
+	}
+
+killdone:
+	switch (res) {
+	case KQT_LFERROR:
+		printf("\tcannot open lock file: %s\n",
+		    strerror(errsav));
+		break;
+	case KQT_NODAEMON:
+		printf("\tno daemon to abort\n");
+		break;
+	case KQT_KILLFAIL:
+	case KQT_KILLOK:
+		/* These two already printed messages to the user. */
+		break;
+	default:
+		printf("\t<internal error in kill_qtask>\n");
+		break;
+	}
+
+	return (res);
+}
+
+/*
   * Write a message into the status file.
   */
  static void
@@ -750,6 +893,21 @@
  }

  /*
+ * Enable queuing to the printer (allow lpr to add new jobs to the queue).
+ */
+void
+enable_q(struct printer *pp)
+{
+	int setres;
+	char lf[MAXPATHLEN];
+
+	lock_file_name(pp, lf, sizeof lf);
+	printf("%s:\n", pp->printer);
+
+	setres = set_qstate(SQS_ENABLEQ, lf);
+}
+
+/*
   * Disable queuing.
   */
  void
@@ -786,6 +944,21 @@
  }

  /*
+ * Disable queuing.
+ */
+void
+disable_q(struct printer *pp)
+{
+	int setres;
+	char lf[MAXPATHLEN];
+
+	lock_file_name(pp, lf, sizeof lf);
+	printf("%s:\n", pp->printer);
+
+	setres = set_qstate(SQS_DISABLEQ, lf);
+}
+
+/*
   * Disable queuing and printing and put a message into the status file
   * (reason for being down).
   */
@@ -924,6 +1097,37 @@
  }

  /*
+ * Kill and restart the daemon.
+ */
+void
+restart_q(struct printer *pp)
+{
+	int killres, setres, startok;
+	char lf[MAXPATHLEN];
+
+	lock_file_name(pp, lf, sizeof lf);
+	printf("%s:\n", pp->printer);
+
+	killres = kill_qtask(lf);
+
+	/*
+	 * XXX - if the kill worked, we should probably sleep for
+	 *      a second or so before trying to restart the queue.
+	 */
+
+	/* make sure the queue is set to print jobs */
+	setres = set_qstate(SQS_STARTP, lf);
+
+	seteuid(euid);
+	startok = startdaemon(pp);
+	seteuid(uid);
+	if (!startok)
+		printf("\tcouldn't restart daemon\n");
+	else
+		printf("\tdaemon restarted\n");
+}
+
+/*
   * Enable printing on the specified printer and startup the daemon.
   */
  void
@@ -962,6 +1166,30 @@
  }

  /*
+ * Enable printing on the specified printer and startup the daemon.
+ */
+void
+start_q(struct printer *pp)
+{
+	int setres, startok;
+	char lf[MAXPATHLEN];
+
+	lock_file_name(pp, lf, sizeof lf);
+	printf("%s:\n", pp->printer);
+
+	setres = set_qstate(SQS_STARTP, lf);
+
+	seteuid(euid);
+	startok = startdaemon(pp);
+	seteuid(uid);
+	if (!startok)
+		printf("\tcouldn't start daemon\n");
+	else
+		printf("\tdaemon started\n");
+	seteuid(uid);
+}
+
+/*
   * Print the status of the printer queue.
   */
  void
@@ -1064,6 +1292,28 @@
  	seteuid(uid);
  }

+/*
+ * Stop the specified daemon after completing the current job and disable
+ * printing.
+ */
+void
+stop_q(struct printer *pp)
+{
+	int setres;
+	char lf[MAXPATHLEN];
+
+	lock_file_name(pp, lf, sizeof lf);
+	printf("%s:\n", pp->printer);
+
+	setres = set_qstate(SQS_STOPP, lf);
+
+	if (setres >= 0) {
+		seteuid(euid);
+		upstat(pp, "printing disabled\n");
+		seteuid(uid);
+	}
+}
+
  struct	jobqueue **queue;
  int	nitems;
  time_t	mtime;
@@ -1236,4 +1486,27 @@
  up(struct printer *pp)
  {
  	startpr(pp, 2);
+}
+
+/*
+ * Enable both queuing & printing, and start printer (undo `down').
+ */
+void
+up_q(struct printer *pp)
+{
+	int setres, startok;
+	char lf[MAXPATHLEN];
+
+	lock_file_name(pp, lf, sizeof lf);
+	printf("%s:\n", pp->printer);
+
+	setres = set_qstate(SQS_ENABLEQ+SQS_STARTP, lf);
+
+	seteuid(euid);
+	startok = startdaemon(pp);
+	seteuid(uid);
+	if (!startok)
+		printf("\tcouldn't start daemon\n");
+	else
+		printf("\tdaemon started\n");
  }
Index: lpc/cmdtab.c
===================================================================
RCS file: /home/ncvs/src/usr.sbin/lpr/lpc/cmdtab.c,v
retrieving revision 1.4
diff -u -r1.4 cmdtab.c
--- lpc/cmdtab.c	25 Jun 2001 02:05:03 -0000	1.4
+++ lpc/cmdtab.c	8 Jun 2002 21:16:56 -0000
@@ -65,21 +65,28 @@
  #define PR	1	/* a privileged command */

  struct cmd cmdtab[] = {
-	{ "abort",	aborthelp,	PR,	0,		doabort },
+	{ "abort",	aborthelp,	PR,	0,		abort_q },
  	{ "clean",	cleanhelp,	PR,	init_clean,	clean_q },
-	{ "enable",	enablehelp,	PR,	0,		enable },
+	{ "enable",	enablehelp,	PR,	0,		enable_q },
  	{ "exit",	quithelp,	0,	quit,		0 },
-	{ "disable",	disablehelp,	PR,	0, 		disable },
+	{ "disable",	disablehelp,	PR,	0, 		disable_q },
  	{ "down",	downhelp,	PR,	down,		0 },
  	{ "help",	helphelp,	0,	help,		0 },
  	{ "quit",	quithelp,	0,	quit,		0 },
-	{ "restart",	restarthelp,	0,	0,		restart },
-	{ "start",	starthelp,	PR,	0,		startcmd },
+	{ "restart",	restarthelp,	0,	0,		restart_q },
+	{ "start",	starthelp,	PR,	0,		start_q },
  	{ "status",	statushelp,	0,	0,		status },
-	{ "stop",	stophelp,	PR,	0,		stop },
+	{ "stop",	stophelp,	PR,	0,		stop_q },
  	{ "tclean",	tcleanhelp,	0,	init_tclean,	clean_q },
  	{ "topq",	topqhelp,	PR,	topq,		0 },
-	{ "up",		uphelp,		PR,	0,		up },
+	{ "up",		uphelp,		PR,	0,		up_q },
+	{ "xabort",	aborthelp,	PR,	0,		doabort },
+	{ "xenable",	enablehelp,	PR,	0,		enable },
+	{ "xdisable",	disablehelp,	PR,	0, 		disable },
+	{ "xrestart",	restarthelp,	0,	0,		restart },
+	{ "xstart",	starthelp,	PR,	0,		startcmd },
+	{ "xstop",	stophelp,	PR,	0,		stop },
+	{ "xup",	uphelp,		PR,	0,		up },
  	{ "?",		helphelp,	0,	help,		0 },
  	{ 0, 0, 0, 0, 0},
  };
Index: lpc/extern.h
===================================================================
RCS file: /home/ncvs/src/usr.sbin/lpr/lpc/extern.h,v
retrieving revision 1.5
diff -u -r1.5 extern.h
--- lpc/extern.h	25 Jun 2001 02:05:03 -0000	1.5
+++ lpc/extern.h	8 Jun 2002 21:16:56 -0000
@@ -42,11 +42,14 @@


  __BEGIN_DECLS
+void	 abort_q(struct printer *_pp);
  void	 clean_q(struct printer *_pp);
  void	 disable(struct printer *_pp);
+void	 disable_q(struct printer *_pp);
  void	 doabort(struct printer *_pp);
  void	 down(int _argc, char *_argv[]);
  void	 enable(struct printer *_pp);
+void	 enable_q(struct printer *_pp);
  void	 generic(void (*_specificrtn)(struct printer *_pp),
  	    void (*_initcmd)(int _argc, char *_argv[]),
  	    int _argc, char *_argv[]);
@@ -55,11 +58,15 @@
  void	 init_tclean(int _argc, char *_argv[]);
  void	 quit(int _argc, char *_argv[]);
  void	 restart(struct printer *_pp);
+void	 restart_q(struct printer *_pp);
  void	 startcmd(struct printer *_pp);
+void	 start_q(struct printer *_pp);
  void	 status(struct printer *_pp);
  void	 stop(struct printer *_pp);
+void	 stop_q(struct printer *_pp);
  void	 topq(int _argc, char *_argv[]);
  void	 up(struct printer *_pp);
+void	 up_q(struct printer *_pp);
  __END_DECLS

  extern int NCMDS;

-- 
Garance Alistair Drosehn            =   gad@gilead.netel.rpi.edu
Senior Systems Programmer           or  gad@freebsd.org
Rensselaer Polytechnic Institute    or  drosih@rpi.edu

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-audit" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?p05111731b928291892ef>