3 * Accepts a connection from port 9100+n and copy stream to
4 * /dev/lpn, where n = 0,1,2.
6 * GPLv2 license, read COPYING
8 * Run standalone as: p910nd [0|1|2]
11 * p910n stream tcp nowait root /usr/sbin/tcpd p910nd [0|1|2]
12 * where p910n is an /etc/services entry for
13 * port 9100, 9101 or 9102 as the case may be.
14 * root can be replaced by any uid with rw permission on /dev/lpn
16 * Port 9100+n will then be passively opened
20 * Patch by Mario Izquierdo
21 * Fix incomplete conversion to manipulate new ip_addr structure
22 * when LIBWRAP is selected
25 * Patch by Guenther Niess:
27 * Patch by Philip Prindeville:
28 * Increase socket buffer size
29 * Use %hu for printing port
30 * Makefile fixes for LIBWRAP
33 * Fix open call to include mode, required for O_CREAT
36 * Patches by Dave Brown. Use raw I/O syscalls instead of
37 * stdio buffering. Buffer system to handle talkative bidi
38 * devices better on low-powered hosts.
41 * Patch by Hans Harder. Close printer device after each use to
42 * avoid crashing when hotpluggable devices going away.
43 * Don't wait 10 seconds after successful open.
46 * Patch by Kostas Liakakis to keep retrying every 10 seconds
47 * if EBUSY is returned by open_printer, apparently NetBSD
48 * does this if the printer is not on.
49 * Patch by Albert Bartoszko (al_bin@vp_pl), August 2006
50 * Work with hotpluggable devices
53 * (The last two patches conflict somewhat, Liakakis's patch
54 * retries opening the device every 10 seconds until successful,
55 * whereas Bartoszko's patch exits if the printer device cannot be opened.
56 * The problem is with a hotpluggable device, that device node may
59 * I have opted for Liakakis's behaviour. Let me know if this can
60 * be improved. Perhaps we need another option that chooses the
64 * Allow specifying address to bind to
67 * Bidirectional data transfer
70 * Arne Bernin fixed some cast warnings, corrected the version number
71 * and added a -v option to print the version.
74 * -DUSE_LIBWRAP and -lwrap enables hosts_access (tcpwrappers) checking.
77 * Ken Yap (greenpossum@users.sourceforge.net), April 2001
80 * Added -f switch to specify device which overrides /dev/lpn.
81 * But number is still required get distinct ports and locks.
83 * Added locking so that two invocations of the daemon under inetd
84 * don't try to open the printer at the same time. This can happen
85 * even if there is one host running clients because the previous
86 * client can exit after it has sent all data but the printer has not
87 * finished printing and inetd starts up a new daemon when the next
88 * request comes in too soon.
90 * Various things could be Linux specific. I don't
91 * think there is much demand for this program outside of PCs,
92 * but if you port it to other distributions or platforms,
93 * I'd be happy to receive your patches.
106 #include <sys/types.h>
107 #include <sys/time.h>
108 #include <sys/resource.h>
109 #include <sys/stat.h>
110 #include <sys/socket.h>
111 #include <netinet/in.h>
112 #include <arpa/inet.h>
116 int allow_severity
, deny_severity
;
117 extern int hosts_ctl(char *daemon
, char *client_name
, char *client_addr
, char *client_user
);
120 #define BASEPORT 9100
121 #define PIDFILE "/var/run/p910%cd.pid"
123 #define LOCKFILE LOCKFILE_DIR "/p910%cd"
125 #define LOCKFILE "/var/lock/subsys/p910%cd"
128 #define PRINTERFILE "/dev/lp%c"
130 #define LOGOPTS LOG_ERR
132 #define BUFFER_SIZE 8192
134 /* Circular buffer used for each direction. */
136 int detectEof
; /* If nonzero, EOF is marked when read returns 0 bytes. */
137 int infd
; /* Input file descriptor. */
138 int outfd
; /* Output file descriptor. */
139 int startidx
; /* Index of the start of valid data. */
140 int endidx
; /* Index of the end of valid data. */
141 int bytes
; /* The number of bytes currently buffered. */
142 int totalin
; /* Total bytes that have been read. */
143 int totalout
; /* Total bytes that have been written. */
144 int eof
; /* Nonzero indicates the input file has reached EOF. */
145 int err
; /* Nonzero indicates an error detected on the output file. */
146 char buffer
[BUFFER_SIZE
]; /* Buffered data goes here. */
149 static char *progname
;
150 static char version
[] = "Version 0.94";
151 static char copyright
[] = "Copyright (c) 2008 Ken Yap, GPLv2";
152 static int lockfd
= -1;
153 static char *device
= 0;
154 static int bidir
= 0;
155 static char *bindaddr
= 0;
158 /* Helper function: convert a struct sockaddr address (IPv4 and IPv6) to a string */
159 char *get_ip_str(const struct sockaddr
*sa
, char *s
, size_t maxlen
)
161 switch(sa
->sa_family
) {
163 inet_ntop(AF_INET
, &(((struct sockaddr_in
*)sa
)->sin_addr
), s
, maxlen
);
166 inet_ntop(AF_INET6
, &(((struct sockaddr_in6
*)sa
)->sin6_addr
), s
, maxlen
);
169 strncpy(s
, "Unknown AF", maxlen
);
175 uint16_t get_port(const struct sockaddr
*sa
)
178 switch(sa
->sa_family
) {
180 port
= ntohs(((struct sockaddr_in
*)sa
)->sin_port
);
183 port
= ntohs(((struct sockaddr_in6
*)sa
)->sin6_port
);
193 fprintf(stderr
, "%s %s %s\n", progname
, version
, copyright
);
194 fprintf(stderr
, "Usage: %s [-f device] [-i bindaddr] [-bv] [0|1|2]\n", progname
);
198 void show_version(void)
200 fprintf(stdout
, "%s %s\n", progname
, version
);
203 int open_printer(int lpnumber
)
206 char lpname
[sizeof(PRINTERFILE
)];
209 (void)snprintf(lpname
, sizeof(lpname
), "/dev/tty");
211 (void)snprintf(lpname
, sizeof(lpname
), PRINTERFILE
, lpnumber
);
215 if ((lp
= open(device
, bidir
? O_RDWR
: O_WRONLY
)) == -1) {
217 syslog(LOGOPTS
, "%s: %m\n", device
);
218 syslog(LOGOPTS
, "%s: %m, will try opening later\n", device
);
223 int get_lock(int lpnumber
)
225 char lockname
[sizeof(LOCKFILE
)];
228 (void)snprintf(lockname
, sizeof(lockname
), LOCKFILE
, lpnumber
);
229 if ((lockfd
= open(lockname
, O_CREAT
| O_RDWR
, 0666)) < 0) {
230 syslog(LOGOPTS
, "%s: %m\n", lockname
);
233 memset(&lplock
, 0, sizeof(lplock
));
234 lplock
.l_type
= F_WRLCK
;
235 lplock
.l_pid
= getpid();
236 if (fcntl(lockfd
, F_SETLKW
, &lplock
) < 0) {
237 syslog(LOGOPTS
, "%s: %m\n", lockname
);
249 /* Initializes the buffer, at the start. */
250 void initBuffer(Buffer_t
* b
, int infd
, int outfd
, int detectEof
)
252 b
->detectEof
= detectEof
;
264 /* Sets the readfds and writefds (used by select) based on current buffer state. */
265 void prepBuffer(Buffer_t
* b
, fd_set
* readfds
, fd_set
* writefds
)
267 if (!b
->err
&& b
->bytes
!= 0) {
268 FD_SET(b
->outfd
, writefds
);
270 if (!b
->eof
&& b
->bytes
< sizeof(b
->buffer
)) {
271 FD_SET(b
->infd
, readfds
);
275 /* Reads data into a buffer from its input file. */
276 ssize_t
readBuffer(Buffer_t
* b
)
280 /* If err, the data will not be written, so no need to store it. */
281 if (b
->bytes
== 0 || b
->err
) {
282 /* The buffer is empty. */
283 b
->startidx
= b
->endidx
= 0;
284 avail
= sizeof(b
->buffer
);
285 } else if (b
->bytes
== sizeof(b
->buffer
)) {
286 /* The buffer is full. */
288 } else if (b
->endidx
> b
->startidx
) {
289 /* The buffer is not wrapped: from endidx to end of buffer is free. */
290 avail
= sizeof(b
->buffer
) - b
->endidx
;
292 /* The buffer is wrapped: gap between endidx and startidx is free. */
293 avail
= b
->startidx
- b
->endidx
;
296 result
= read(b
->infd
, b
->buffer
+ b
->endidx
, avail
);
298 /* Some data was read. Update accordingly. */
300 b
->totalin
+= result
;
302 if (b
->endidx
== sizeof(b
->buffer
)) {
303 /* Time to wrap the buffer. */
306 } else if (result
< 0 || b
->detectEof
) {
307 /* Mark end-of-file on input error, or on zero return if detectEof. */
311 /* Return the value returned by read(), which is -1, 0, or #bytes read. */
315 /* Writes data from a buffer to the output file. */
316 ssize_t
writeBuffer(Buffer_t
* b
)
320 if (b
->bytes
== 0 || b
->err
) {
321 /* Buffer is empty. */
323 } else if (b
->endidx
> b
->startidx
) {
324 /* Buffer is not wrapped. Can write all the data. */
325 avail
= b
->endidx
- b
->startidx
;
327 /* Buffer is wrapped. Can only write the top (first) part. */
328 avail
= sizeof(b
->buffer
) - b
->startidx
;
331 result
= write(b
->outfd
, b
->buffer
+ b
->startidx
, avail
);
333 /* Mark the output file in an error condition. */
334 syslog(LOGOPTS
, "write: %m\n");
337 /* Zero or more bytes were written. */
338 b
->startidx
+= result
;
339 b
->totalout
+= result
;
341 if (b
->startidx
== sizeof(b
->buffer
)) {
342 /* Unwrap the buffer. */
347 /* Return the write() result, -1 (error), or #bytes written. */
351 /* Copy network data from file descriptor fd (network) to lp (printer) until EOS */
352 /* If bidir, also copy data from printer (lp) to network (fd). */
353 int copy_stream(int fd
, int lp
)
356 Buffer_t networkToPrinterBuffer
;
357 initBuffer(&networkToPrinterBuffer
, fd
, lp
, 1);
362 struct timeval timeout
;
364 Buffer_t printerToNetworkBuffer
;
365 initBuffer(&printerToNetworkBuffer
, lp
, fd
, 0);
368 /* Initially read from both streams, don't write to either. */
371 FD_SET(lp
, &readfds
);
372 FD_SET(fd
, &readfds
);
373 /* Finish when no longer reading fd, and no longer writing to lp. */
374 /* Although the printer to network stream may not be finished, that does not matter. */
375 while ((FD_ISSET(fd
, &readfds
)) || (FD_ISSET(lp
, &writefds
))) {
376 int maxfd
= lp
> fd
? lp
: fd
;
378 /* Delay after reading from the printer, so the */
379 /* return stream cannot dominate. */
380 /* Don't read from the printer until the timer expires. */
381 gettimeofday(&now
, 0);
382 if ((now
.tv_sec
> then
.tv_sec
) || (now
.tv_sec
== then
.tv_sec
&& now
.tv_usec
> then
.tv_usec
)) {
385 timeout
.tv_sec
= then
.tv_sec
;
386 timeout
.tv_usec
= then
.tv_usec
;
387 FD_CLR(lp
, &readfds
);
391 result
= select(maxfd
+ 1, &readfds
, &writefds
, 0, &timeout
);
393 result
= select(maxfd
+ 1, &readfds
, &writefds
, 0, 0);
397 if (FD_ISSET(fd
, &readfds
)) {
398 /* Read network data. */
399 result
= readBuffer(&networkToPrinterBuffer
);
401 if (FD_ISSET(lp
, &readfds
)) {
402 /* Read printer data, but pace it more slowly. */
403 result
= readBuffer(&printerToNetworkBuffer
);
405 gettimeofday(&then
, 0);
406 // wait 100 msec before reading again.
407 then
.tv_usec
+= 100000;
408 if (then
.tv_usec
> 1000000) {
409 then
.tv_usec
-= 1000000;
415 if (FD_ISSET(lp
, &writefds
)) {
416 /* Write data to printer. */
417 result
= writeBuffer(&networkToPrinterBuffer
);
419 if (FD_ISSET(fd
, &writefds
)) {
420 /* Write data to network. */
421 result
= writeBuffer(&printerToNetworkBuffer
);
422 /* If socket write error, stop reading from printer */
424 networkToPrinterBuffer
.eof
= 1;
426 /* Prepare for next iteration. */
429 prepBuffer(&networkToPrinterBuffer
, &readfds
, &writefds
);
430 prepBuffer(&printerToNetworkBuffer
, &readfds
, &writefds
);
433 "Finished job: %d bytes received, %d bytes sent\n",
434 networkToPrinterBuffer
.totalout
, printerToNetworkBuffer
.totalout
);
437 /* Unidirectional: simply read from network, and write to printer. */
438 while ((result
= readBuffer(&networkToPrinterBuffer
)) > 0) {
439 (void)writeBuffer(&networkToPrinterBuffer
);
441 syslog(LOG_NOTICE
, "Finished job: %d bytes received\n", networkToPrinterBuffer
.totalout
);
446 void one_job(int lpnumber
)
449 struct sockaddr_storage client
;
450 socklen_t clientlen
= sizeof(client
);
452 if (getpeername(0, (struct sockaddr
*)&client
, &clientlen
) >= 0) {
453 char host
[INET6_ADDRSTRLEN
];
454 syslog(LOG_NOTICE
, "Connection from %s port %hu\n", get_ip_str((struct sockaddr
*)&client
, host
, sizeof(host
)), get_port((struct sockaddr
*)&client
));
456 if (get_lock(lpnumber
) == 0)
458 /* Make sure lp device is open... */
459 while ((lp
= open_printer(lpnumber
)) == -1)
461 if (copy_stream(0, lp
) < 0)
462 syslog(LOGOPTS
, "copy_stream: %m\n");
467 void server(int lpnumber
)
469 struct rlimit resourcelimit
;
470 #ifdef USE_GETPROTOBYNAME
471 struct protoent
*proto
;
473 int netfd
= -1, fd
, lp
, one
= 1;
475 struct sockaddr_storage client
;
476 struct addrinfo hints
, *res
, *ressave
;
477 char pidfilename
[sizeof(PIDFILE
)];
478 char service
[sizeof(BASEPORT
+lpnumber
-'0')+1];
480 const int bufsiz
= 65536;
485 syslog(LOGOPTS
, "fork: %m\n");
489 default: /* parent */
492 /* Now in child process */
493 resourcelimit
.rlim_max
= 0;
494 if (getrlimit(RLIMIT_NOFILE
, &resourcelimit
) < 0) {
495 syslog(LOGOPTS
, "getrlimit: %m\n");
498 for (fd
= 0; fd
< resourcelimit
.rlim_max
; ++fd
)
501 syslog(LOGOPTS
, "setsid: %m\n");
506 fd
= open("/dev/null", O_RDWR
); /* stdin */
507 (void)dup(fd
); /* stdout */
508 (void)dup(fd
); /* stderr */
509 (void)snprintf(pidfilename
, sizeof(pidfilename
), PIDFILE
, lpnumber
);
510 if ((f
= fopen(pidfilename
, "w")) == NULL
) {
511 syslog(LOGOPTS
, "%s: %m\n", pidfilename
);
514 (void)fprintf(f
, "%d\n", getpid());
516 if (get_lock(lpnumber
) == 0)
519 memset(&hints
, 0, sizeof(hints
));
520 hints
.ai_family
= PF_UNSPEC
;
521 hints
.ai_flags
= AI_PASSIVE
;
522 hints
.ai_socktype
= SOCK_STREAM
;
523 (void)snprintf(service
, sizeof(service
), "%hu", (BASEPORT
+ lpnumber
- '0'));
524 if (getaddrinfo(bindaddr
, service
, &hints
, &res
) != 0) {
525 syslog(LOGOPTS
, "getaddr: %m\n");
530 #ifdef USE_GETPROTOBYNAME
531 if ((proto
= getprotobyname("tcp6")) == NULL
) {
532 if ((proto
= getprotobyname("tcp")) == NULL
) {
533 syslog(LOGOPTS
, "Cannot find protocol for TCP!\n");
537 if ((netfd
= socket(res
->ai_family
, res
->ai_socktype
, proto
->p_proto
)) < 0)
539 if ((netfd
= socket(res
->ai_family
, res
->ai_socktype
, IPPROTO_IP
)) < 0)
542 syslog(LOGOPTS
, "socket: %m\n");
547 if (setsockopt(netfd
, SOL_SOCKET
, SO_RCVBUF
, &bufsiz
, sizeof(bufsiz
)) < 0) {
548 syslog(LOGOPTS
, "setsocketopt: SO_RCVBUF: %m\n");
549 /* not fatal if it fails */
551 if (setsockopt(netfd
, SOL_SOCKET
, SO_SNDBUF
, &bufsiz
, sizeof(bufsiz
)) < 0) {
552 syslog(LOGOPTS
, "setsocketopt: SO_SNDBUF: %m\n");
553 /* not fatal if it fails */
555 if (setsockopt(netfd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
)) < 0) {
556 syslog(LOGOPTS
, "setsocketopt: SO_REUSEADDR: %m\n");
561 if (bind(netfd
, res
->ai_addr
, res
->ai_addrlen
) < 0) {
562 syslog(LOGOPTS
, "bind: %m\n");
567 if (listen(netfd
, 5) < 0) {
568 syslog(LOGOPTS
, "listen: %m\n");
575 freeaddrinfo(ressave
);
576 clientlen
= sizeof(client
);
577 memset(&client
, 0, sizeof(client
));
578 while ((fd
= accept(netfd
, (struct sockaddr
*)&client
, &clientlen
)) >= 0) {
579 char host
[INET6_ADDRSTRLEN
];
581 if (hosts_ctl("p910nd", STRING_UNKNOWN
, get_ip_str((struct sockaddr
*)&client
, host
, sizeof(host
)), STRING_UNKNOWN
) == 0) {
583 "Connection from %s port %hu rejected\n", get_ip_str((struct sockaddr
*)&client
, host
, sizeof(host
)), get_port((struct sockaddr
*)&client
));
588 syslog(LOG_NOTICE
, "Connection from %s port %hu accepted\n", get_ip_str((struct sockaddr
*)&client
, host
, sizeof(host
)), get_port((struct sockaddr
*)&client
));
589 /*write(fd, "Printing", 8); */
591 /* Make sure lp device is open... */
592 while ((lp
= open_printer(lpnumber
)) == -1)
595 if (copy_stream(fd
, lp
) < 0)
596 syslog(LOGOPTS
, "copy_stream: %m\n");
600 syslog(LOGOPTS
, "accept: %m\n");
605 int is_standalone(void)
607 struct sockaddr_storage bind_addr
;
611 * Check to see if a socket was passed to us from (x)inetd.
613 * Use getsockname() to determine if descriptor 0 is indeed a socket
614 * (and thus we are probably a child of (x)inetd) or if it is instead
615 * something else and we are running standalone.
617 ba_len
= sizeof(bind_addr
);
618 if (getsockname(0, (struct sockaddr
*)&bind_addr
, &ba_len
) == 0)
619 return (0); /* under (x)inetd */
620 if (errno
!= ENOTSOCK
) /* strange... */
621 syslog(LOGOPTS
, "getsockname: %m\n");
625 int main(int argc
, char *argv
[])
630 if (argc
<= 0) /* in case not provided in (x)inetd config */
634 if ((p
= strrchr(progname
, '/')) != 0)
638 while ((c
= getopt(argc
, argv
, "bi:f:v")) != EOF
) {
660 if (isdigit(argv
[0][0]))
661 lpnumber
= argv
[0][0];
663 /* change the n in argv[0] to match the port so ps will show that */
664 if ((p
= strstr(progname
, "p910n")) != NULL
)
667 /* We used to pass (LOG_PERROR|LOG_PID|LOG_LPR|LOG_ERR) to syslog, but
668 * syslog ignored the LOG_PID and LOG_PERROR option. I.e. the intention
669 * was to add both options but the effect was to have neither.
670 * I disagree with the intention to add PERROR. --Stef */
671 openlog(p
, LOG_PID
, LOG_LPR
);