1  /*
       2   * Copyright (C) 1992-1997 Michael K. Johnson, johnsonm@redhat.com
       3   *
       4   * This file is licensed under the terms of the GNU General Public
       5   * License, version 2, or any later version.  See file COPYING for
       6   * information on distribution conditions.
       7   */
       8  
       9  /*
      10   * This command is deprecated.  The utility is in maintenance mode,
      11   * meaning we keep them in source tree for backward compatibility
      12   * only.  Do not waste time making this command better, unless the
      13   * fix is about security or other very critical issue.
      14   *
      15   * See Documentation/deprecated.txt for more information.
      16   */
      17  
      18  /*
      19   * $Log: tunelp.c,v $
      20   * Revision 1.9  1998/06/08 19:37:11  janl
      21   * Thus compiles tunelp with 2.1.103 kernels
      22   *
      23   * Revision 1.8  1997/07/06 00:14:06  aebr
      24   * Fixes to silence -Wall.
      25   *
      26   * Revision 1.7  1997/06/20 16:10:38  janl
      27   * tunelp refreshed from authors archive.
      28   *
      29   * Revision 1.9  1997/06/20 12:56:43  johnsonm
      30   * Finished fixing license terms.
      31   *
      32   * Revision 1.8  1997/06/20 12:34:59  johnsonm
      33   * Fixed copyright and license.
      34   *
      35   * Revision 1.7  1995/03/29 11:16:23  johnsonm
      36   * TYPO fixed...
      37   *
      38   * Revision 1.6  1995/03/29  11:12:15  johnsonm
      39   * Added third argument to ioctl needed with new kernels
      40   *
      41   * Revision 1.5  1995/01/13  10:33:43  johnsonm
      42   * Chris's changes for new ioctl numbers and backwards compatibility
      43   * and the reset ioctl.
      44   *
      45   * Revision 1.4  1995/01/03  17:42:14  johnsonm
      46   * -s isn't supposed to take an argument; removed : after s in getopt...
      47   *
      48   * Revision 1.3  1995/01/03  07:36:49  johnsonm
      49   * Fixed typo
      50   *
      51   * Revision 1.2  1995/01/03  07:33:44  johnsonm
      52   * revisions for lp driver updates in Linux 1.1.76
      53   *
      54   * 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL>
      55   * - added Native Language Support
      56   *
      57   * 1999-05-07 Merged LPTRUSTIRQ patch by Andrea Arcangeli (1998/11/29), aeb
      58   *
      59   */
      60  
      61  #include <errno.h>
      62  #include <fcntl.h>
      63  #include <getopt.h>
      64  #include <stdio.h>
      65  #include <stdlib.h>
      66  #include <string.h>
      67  #include <sys/ioctl.h>
      68  #include <sys/stat.h>
      69  #include <sys/types.h>
      70  #include <unistd.h>
      71  
      72  #include <linux/lp.h>
      73  
      74  #include "nls.h"
      75  #include "closestream.h"
      76  #include "strutils.h"
      77  
      78  #define EXIT_LP_MALLOC		2
      79  #define EXIT_LP_BADVAL		3
      80  #define EXIT_LP_IO_ERR		4
      81  
      82  #define XALLOC_EXIT_CODE EXIT_LP_MALLOC
      83  #include "xalloc.h"
      84  
      85  struct command {
      86  	long op;
      87  	long val;
      88  	struct command *next;
      89  };
      90  
      91  static void __attribute__((__noreturn__)) usage(void)
      92  {
      93  	FILE *out = stdout;
      94  	fputs(USAGE_HEADER, out);
      95  	fprintf(out, _(" %s [options] <device>\n"), program_invocation_short_name);
      96  
      97  	fputs(USAGE_SEPARATOR, out);
      98  	fputs(_("Set various parameters for the line printer.\n"), out);
      99  
     100  	fputs(USAGE_OPTIONS, out);
     101  	fputs(_(" -i, --irq <num>              specify parallel port irq\n"), out);
     102  	fputs(_(" -t, --time <ms>              driver wait time in milliseconds\n"), out);
     103  	fputs(_(" -c, --chars <num>            number of output characters before sleep\n"), out);
     104  	fputs(_(" -w, --wait <us>              strobe wait in micro seconds\n"), out);
     105  	/* TRANSLATORS: do not translate <on|off> arguments. The
     106  	   argument reader does not recognize locale, unless `on' is
     107  	   exactly that very same string. */
     108  	fputs(_(" -a, --abort <on|off>         abort on error\n"), out);
     109  	fputs(_(" -o, --check-status <on|off>  check printer status before printing\n"), out);
     110  	fputs(_(" -C, --careful <on|off>       extra checking to status check\n"), out);
     111  	fputs(_(" -s, --status                 query printer status\n"), out);
     112  	fputs(_(" -r, --reset                  reset the port\n"), out);
     113  	fputs(_(" -q, --print-irq <on|off>     display current irq setting\n"), out);
     114  	fputs(USAGE_SEPARATOR, out);
     115  	printf(USAGE_HELP_OPTIONS(30));
     116  	printf(USAGE_MAN_TAIL("tunelp(8)"));
     117  
     118  	exit(EXIT_SUCCESS);
     119  }
     120  
     121  int main(int argc, char **argv)
     122  {
     123  	int c, fd, irq, status, show_irq, offset = 0, retval;
     124  	char *filename;
     125  	struct stat statbuf;
     126  	struct command *cmds, *cmdst;
     127  	static const struct option longopts[] = {
     128  		{"irq", required_argument, NULL, 'i'},
     129  		{"time", required_argument, NULL, 't'},
     130  		{"chars", required_argument, NULL, 'c'},
     131  		{"wait", required_argument, NULL, 'w'},
     132  		{"abort", required_argument, NULL, 'a'},
     133  		{"check-status", required_argument, NULL, 'o'},
     134  		{"careful", required_argument, NULL, 'C'},
     135  		{"status", no_argument, NULL, 's'},
     136  		{"trust-irq", required_argument, NULL, 'T'},
     137  		{"reset", no_argument, NULL, 'r'},
     138  		{"print-irq", required_argument, NULL, 'q'},
     139  		{"version", no_argument, NULL, 'V'},
     140  		{"help", no_argument, NULL, 'h'},
     141  		{NULL, 0, NULL, 0}
     142  	};
     143  
     144  	setlocale(LC_ALL, "");
     145  	bindtextdomain(PACKAGE, LOCALEDIR);
     146  	textdomain(PACKAGE);
     147  	close_stdout_atexit();
     148  
     149  	strutils_set_exitcode(EXIT_LP_BADVAL);
     150  
     151  	if (argc < 2) {
     152  		warnx(_("not enough arguments"));
     153  		errtryhelp(EXIT_FAILURE);
     154  	}
     155  
     156  	cmdst = cmds = xmalloc(sizeof(struct command));
     157  	cmds->next = NULL;
     158  
     159  	show_irq = 1;
     160  	while ((c = getopt_long(argc, argv, "t:c:w:a:i:ho:C:sq:rT:vV", longopts, NULL)) != -1) {
     161  		switch (c) {
     162  		case 'i':
     163  			cmds->op = LPSETIRQ;
     164  			cmds->val = strtol_or_err(optarg, _("argument error"));
     165  			cmds->next = xmalloc(sizeof(struct command));
     166  			cmds = cmds->next;
     167  			cmds->next = NULL;
     168  			break;
     169  		case 't':
     170  			cmds->op = LPTIME;
     171  			cmds->val = strtol_or_err(optarg, _("argument error"));
     172  			cmds->next = xmalloc(sizeof(struct command));
     173  			cmds = cmds->next;
     174  			cmds->next = NULL;
     175  			break;
     176  		case 'c':
     177  			cmds->op = LPCHAR;
     178  			cmds->val = strtol_or_err(optarg, _("argument error"));
     179  			cmds->next = xmalloc(sizeof(struct command));
     180  			cmds = cmds->next;
     181  			cmds->next = NULL;
     182  			break;
     183  		case 'w':
     184  			cmds->op = LPWAIT;
     185  			cmds->val = strtol_or_err(optarg, _("argument error"));
     186  			cmds->next = xmalloc(sizeof(struct command));
     187  			cmds = cmds->next;
     188  			cmds->next = NULL;
     189  			break;
     190  		case 'a':
     191  			cmds->op = LPABORT;
     192  			cmds->val = parse_switch(optarg, _("argument error"), "on", "off", NULL);
     193  			cmds->next = xmalloc(sizeof(struct command));
     194  			cmds = cmds->next;
     195  			cmds->next = NULL;
     196  			break;
     197  		case 'q':
     198  			show_irq = parse_switch(optarg, _("argument error"), "on", "off", NULL);
     199  			break;
     200  		case 'o':
     201  			cmds->op = LPABORTOPEN;
     202  			cmds->val = parse_switch(optarg, _("argument error"), "on", "off", NULL);
     203  			cmds->next = xmalloc(sizeof(struct command));
     204  			cmds = cmds->next;
     205  			cmds->next = NULL;
     206  			break;
     207  		case 'C':
     208  			cmds->op = LPCAREFUL;
     209  			cmds->val = parse_switch(optarg, _("argument error"), "on", "off", NULL);
     210  			cmds->next = xmalloc(sizeof(struct command));
     211  			cmds = cmds->next;
     212  			cmds->next = NULL;
     213  			break;
     214  		case 's':
     215  			show_irq = 0;
     216  			cmds->op = LPGETSTATUS;
     217  			cmds->val = 0;
     218  			cmds->next = xmalloc(sizeof(struct command));
     219  			cmds = cmds->next;
     220  			cmds->next = NULL;
     221  			break;
     222  		case 'r':
     223  			cmds->op = LPRESET;
     224  			cmds->val = 0;
     225  			cmds->next = xmalloc(sizeof(struct command));
     226  			cmds = cmds->next;
     227  			cmds->next = NULL;
     228  			break;
     229  
     230  		case 'h':
     231  			usage();
     232  		case 'v':
     233  		case 'V':
     234  			print_version(EXIT_SUCCESS);
     235  		default:
     236  			errtryhelp(EXIT_FAILURE);
     237  		}
     238  	}
     239  
     240  	if (optind != argc - 1) {
     241  		warnx(_("no device specified"));
     242  		errtryhelp(EXIT_FAILURE);
     243  	}
     244  
     245  	filename = xstrdup(argv[optind]);
     246  	fd = open(filename, O_WRONLY | O_NONBLOCK, 0);
     247  	/* Need to open O_NONBLOCK in case ABORTOPEN is already set
     248  	 * and printer is off or off-line or in an error condition.
     249  	 * Otherwise we would abort...
     250           */
     251  	if (fd < 0)
     252  		err(EXIT_FAILURE, "%s", filename);
     253  
     254  	if (fstat(fd, &statbuf))
     255  		err(EXIT_FAILURE, "%s: stat() failed", filename);
     256  
     257  	if (!S_ISCHR(statbuf.st_mode)) {
     258  		warnx(_("%s not an lp device"), filename);
     259  		errtryhelp(EXIT_FAILURE);
     260  	}
     261  	/* Allow for binaries compiled under a new kernel to work on
     262  	 * the old ones The irq argument to ioctl isn't touched by
     263  	 * the old kernels, but we don't want to cause the kernel to
     264  	 * complain if we are using a new kernel
     265  	 */
     266  	if (LPGETIRQ >= 0x0600 && ioctl(fd, LPGETIRQ, &irq) < 0
     267  	    && errno == EINVAL)
     268  	        /* We don't understand the new ioctls */
     269  		offset = 0x0600;
     270  
     271  	cmds = cmdst;
     272  	while (cmds->next) {
     273  		if (cmds->op == LPGETSTATUS) {
     274  			status = 0xdeadbeef;
     275  			retval = ioctl(fd, LPGETSTATUS - offset, &status);
     276  			if (retval < 0)
     277  				warnx(_("LPGETSTATUS error"));
     278  			else {
     279  				if (status == (int)0xdeadbeef)
     280  					/* a few 1.1.7x kernels will do this */
     281  					status = retval;
     282  				printf(_("%s status is %d"), filename, status);
     283  				if (!(status & LP_PBUSY))
     284  					printf(_(", busy"));
     285  				if (!(status & LP_PACK))
     286  					printf(_(", ready"));
     287  				if ((status & LP_POUTPA))
     288  					printf(_(", out of paper"));
     289  				if ((status & LP_PSELECD))
     290  					printf(_(", on-line"));
     291  				if (!(status & LP_PERRORP))
     292  					printf(_(", error"));
     293  				printf("\n");
     294  			}
     295  		} else if (ioctl(fd, cmds->op - offset, cmds->val) < 0)
     296  			warn(_("ioctl failed"));
     297  		cmdst = cmds;
     298  		cmds = cmds->next;
     299  		free(cmdst);
     300  	}
     301  
     302  	if (show_irq) {
     303  		irq = 0xdeadbeef;
     304  		retval = ioctl(fd, LPGETIRQ - offset, &irq);
     305  		if (retval == -1)
     306  			err(EXIT_LP_IO_ERR, _("LPGETIRQ error"));
     307  		if (irq == (int)0xdeadbeef)
     308  		        /* up to 1.1.77 will do this */
     309  			irq = retval;
     310  		if (irq)
     311  			printf(_("%s using IRQ %d\n"), filename, irq);
     312  		else
     313  			printf(_("%s using polling\n"), filename);
     314  	}
     315  	free(filename);
     316  	close(fd);
     317  
     318  	return EXIT_SUCCESS;
     319  }