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.95";
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, will try opening later\n", device
);
222 int get_lock(int lpnumber
)
224 char lockname
[sizeof(LOCKFILE
)];
227 (void)snprintf(lockname
, sizeof(lockname
), LOCKFILE
, lpnumber
);
228 if ((lockfd
= open(lockname
, O_CREAT
| O_RDWR
, 0666)) < 0) {
229 syslog(LOGOPTS
, "%s: %m\n", lockname
);
232 memset(&lplock
, 0, sizeof(lplock
));
233 lplock
.l_type
= F_WRLCK
;
234 lplock
.l_pid
= getpid();
235 if (fcntl(lockfd
, F_SETLKW
, &lplock
) < 0) {
236 syslog(LOGOPTS
, "%s: %m\n", lockname
);
248 /* Initializes the buffer, at the start. */
249 void initBuffer(Buffer_t
* b
, int infd
, int outfd
, int detectEof
)
251 b
->detectEof
= detectEof
;
263 /* Sets the readfds and writefds (used by select) based on current buffer state. */
264 void prepBuffer(Buffer_t
* b
, fd_set
* readfds
, fd_set
* writefds
)
266 if (!b
->err
&& b
->bytes
!= 0) {
267 FD_SET(b
->outfd
, writefds
);
269 if (!b
->eof
&& b
->bytes
< sizeof(b
->buffer
)) {
270 FD_SET(b
->infd
, readfds
);
274 /* Reads data into a buffer from its input file. */
275 ssize_t
readBuffer(Buffer_t
* b
)
279 /* If err, the data will not be written, so no need to store it. */
280 if (b
->bytes
== 0 || b
->err
) {
281 /* The buffer is empty. */
282 b
->startidx
= b
->endidx
= 0;
283 avail
= sizeof(b
->buffer
);
284 } else if (b
->bytes
== sizeof(b
->buffer
)) {
285 /* The buffer is full. */
287 } else if (b
->endidx
> b
->startidx
) {
288 /* The buffer is not wrapped: from endidx to end of buffer is free. */
289 avail
= sizeof(b
->buffer
) - b
->endidx
;
291 /* The buffer is wrapped: gap between endidx and startidx is free. */
292 avail
= b
->startidx
- b
->endidx
;
295 result
= read(b
->infd
, b
->buffer
+ b
->endidx
, avail
);
297 /* Some data was read. Update accordingly. */
299 b
->totalin
+= result
;
301 if (b
->endidx
== sizeof(b
->buffer
)) {
302 /* Time to wrap the buffer. */
305 } else if (result
< 0 || b
->detectEof
) {
306 /* Mark end-of-file on input error, or on zero return if detectEof. */
310 /* Return the value returned by read(), which is -1, 0, or #bytes read. */
314 /* Writes data from a buffer to the output file. */
315 ssize_t
writeBuffer(Buffer_t
* b
)
319 if (b
->bytes
== 0 || b
->err
) {
320 /* Buffer is empty. */
322 } else if (b
->endidx
> b
->startidx
) {
323 /* Buffer is not wrapped. Can write all the data. */
324 avail
= b
->endidx
- b
->startidx
;
326 /* Buffer is wrapped. Can only write the top (first) part. */
327 avail
= sizeof(b
->buffer
) - b
->startidx
;
330 result
= write(b
->outfd
, b
->buffer
+ b
->startidx
, avail
);
332 /* Mark the output file in an error condition. */
333 syslog(LOGOPTS
, "write: %m\n");
336 /* Zero or more bytes were written. */
337 b
->startidx
+= result
;
338 b
->totalout
+= result
;
340 if (b
->startidx
== sizeof(b
->buffer
)) {
341 /* Unwrap the buffer. */
346 /* Return the write() result, -1 (error), or #bytes written. */
350 /* Copy network data from file descriptor fd (network) to lp (printer) until EOS */
351 /* If bidir, also copy data from printer (lp) to network (fd). */
352 int copy_stream(int fd
, int lp
)
355 Buffer_t networkToPrinterBuffer
;
356 initBuffer(&networkToPrinterBuffer
, fd
, lp
, 1);
361 struct timeval timeout
;
363 Buffer_t printerToNetworkBuffer
;
364 initBuffer(&printerToNetworkBuffer
, lp
, fd
, 0);
367 /* Initially read from both streams, don't write to either. */
370 FD_SET(lp
, &readfds
);
371 FD_SET(fd
, &readfds
);
372 /* Finish when no longer reading fd, and no longer writing to lp. */
373 /* Although the printer to network stream may not be finished, that does not matter. */
374 while ((FD_ISSET(fd
, &readfds
)) || (FD_ISSET(lp
, &writefds
))) {
375 int maxfd
= lp
> fd
? lp
: fd
;
377 /* Delay after reading from the printer, so the */
378 /* return stream cannot dominate. */
379 /* Don't read from the printer until the timer expires. */
380 gettimeofday(&now
, 0);
381 if ((now
.tv_sec
> then
.tv_sec
) || (now
.tv_sec
== then
.tv_sec
&& now
.tv_usec
> then
.tv_usec
)) {
384 timeout
.tv_sec
= then
.tv_sec
- now
.tv_sec
;
385 timeout
.tv_usec
= then
.tv_usec
- now
.tv_usec
;
386 if (timeout
.tv_usec
< 0) {
387 timeout
.tv_usec
+= 1000000;
390 FD_CLR(lp
, &readfds
);
394 result
= select(maxfd
+ 1, &readfds
, &writefds
, 0, &timeout
);
396 result
= select(maxfd
+ 1, &readfds
, &writefds
, 0, 0);
400 if (FD_ISSET(fd
, &readfds
)) {
401 /* Read network data. */
402 result
= readBuffer(&networkToPrinterBuffer
);
404 if (FD_ISSET(lp
, &readfds
)) {
405 /* Read printer data, but pace it more slowly. */
406 result
= readBuffer(&printerToNetworkBuffer
);
408 gettimeofday(&then
, 0);
409 // wait 100 msec before reading again.
410 then
.tv_usec
+= 100000;
411 if (then
.tv_usec
> 1000000) {
412 then
.tv_usec
-= 1000000;
418 if (FD_ISSET(lp
, &writefds
)) {
419 /* Write data to printer. */
420 result
= writeBuffer(&networkToPrinterBuffer
);
422 if (FD_ISSET(fd
, &writefds
)) {
423 /* Write data to network. */
424 result
= writeBuffer(&printerToNetworkBuffer
);
425 /* If socket write error, stop reading from printer */
427 networkToPrinterBuffer
.eof
= 1;
429 /* Prepare for next iteration. */
432 prepBuffer(&networkToPrinterBuffer
, &readfds
, &writefds
);
433 prepBuffer(&printerToNetworkBuffer
, &readfds
, &writefds
);
436 "Finished job: %d bytes received, %d bytes sent\n",
437 networkToPrinterBuffer
.totalout
, printerToNetworkBuffer
.totalout
);
440 /* Unidirectional: simply read from network, and write to printer. */
441 while ((result
= readBuffer(&networkToPrinterBuffer
)) > 0) {
442 (void)writeBuffer(&networkToPrinterBuffer
);
444 syslog(LOG_NOTICE
, "Finished job: %d bytes received\n", networkToPrinterBuffer
.totalout
);
449 void one_job(int lpnumber
)
453 struct sockaddr_storage client
;
454 socklen_t clientlen
= sizeof(client
);
456 if (getpeername(0, (struct sockaddr
*)&client
, &clientlen
) >= 0) {
457 char host
[INET6_ADDRSTRLEN
];
458 syslog(LOG_NOTICE
, "Connection from %s port %hu\n", get_ip_str((struct sockaddr
*)&client
, host
, sizeof(host
)), get_port((struct sockaddr
*)&client
));
460 if (get_lock(lpnumber
) == 0)
462 /* Make sure lp device is open... */
463 while ((lp
= open_printer(lpnumber
)) == -1) {
465 if (open_sleep
< 320) /* ~5 min interval to avoid spam in syslog */
468 if (copy_stream(0, lp
) < 0)
469 syslog(LOGOPTS
, "copy_stream: %m\n");
474 void server(int lpnumber
)
476 struct rlimit resourcelimit
;
477 #ifdef USE_GETPROTOBYNAME
478 struct protoent
*proto
;
480 int netfd
= -1, fd
, lp
, one
= 1;
483 struct sockaddr_storage client
;
484 struct addrinfo hints
, *res
, *ressave
;
485 char pidfilename
[sizeof(PIDFILE
)];
486 char service
[sizeof(BASEPORT
+lpnumber
-'0')+1];
488 const int bufsiz
= 65536;
493 syslog(LOGOPTS
, "fork: %m\n");
497 default: /* parent */
500 /* Now in child process */
501 resourcelimit
.rlim_max
= 0;
502 if (getrlimit(RLIMIT_NOFILE
, &resourcelimit
) < 0) {
503 syslog(LOGOPTS
, "getrlimit: %m\n");
506 for (fd
= 0; fd
< resourcelimit
.rlim_max
; ++fd
)
509 syslog(LOGOPTS
, "setsid: %m\n");
514 fd
= open("/dev/null", O_RDWR
); /* stdin */
515 (void)dup(fd
); /* stdout */
516 (void)dup(fd
); /* stderr */
517 (void)snprintf(pidfilename
, sizeof(pidfilename
), PIDFILE
, lpnumber
);
518 if ((f
= fopen(pidfilename
, "w")) == NULL
) {
519 syslog(LOGOPTS
, "%s: %m\n", pidfilename
);
522 (void)fprintf(f
, "%d\n", getpid());
524 if (get_lock(lpnumber
) == 0)
527 memset(&hints
, 0, sizeof(hints
));
528 hints
.ai_family
= PF_UNSPEC
;
529 hints
.ai_flags
= AI_PASSIVE
;
530 hints
.ai_socktype
= SOCK_STREAM
;
531 (void)snprintf(service
, sizeof(service
), "%hu", (BASEPORT
+ lpnumber
- '0'));
532 if (getaddrinfo(bindaddr
, service
, &hints
, &res
) != 0) {
533 syslog(LOGOPTS
, "getaddr: %m\n");
538 #ifdef USE_GETPROTOBYNAME
539 if ((proto
= getprotobyname("tcp6")) == NULL
) {
540 if ((proto
= getprotobyname("tcp")) == NULL
) {
541 syslog(LOGOPTS
, "Cannot find protocol for TCP!\n");
545 if ((netfd
= socket(res
->ai_family
, res
->ai_socktype
, proto
->p_proto
)) < 0)
547 if ((netfd
= socket(res
->ai_family
, res
->ai_socktype
, IPPROTO_IP
)) < 0)
550 syslog(LOGOPTS
, "socket: %m\n");
555 if (setsockopt(netfd
, SOL_SOCKET
, SO_RCVBUF
, &bufsiz
, sizeof(bufsiz
)) < 0) {
556 syslog(LOGOPTS
, "setsocketopt: SO_RCVBUF: %m\n");
557 /* not fatal if it fails */
559 if (setsockopt(netfd
, SOL_SOCKET
, SO_SNDBUF
, &bufsiz
, sizeof(bufsiz
)) < 0) {
560 syslog(LOGOPTS
, "setsocketopt: SO_SNDBUF: %m\n");
561 /* not fatal if it fails */
563 if (setsockopt(netfd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
)) < 0) {
564 syslog(LOGOPTS
, "setsocketopt: SO_REUSEADDR: %m\n");
569 if (bind(netfd
, res
->ai_addr
, res
->ai_addrlen
) < 0) {
570 syslog(LOGOPTS
, "bind: %m\n");
575 if (listen(netfd
, 5) < 0) {
576 syslog(LOGOPTS
, "listen: %m\n");
583 freeaddrinfo(ressave
);
584 clientlen
= sizeof(client
);
585 memset(&client
, 0, sizeof(client
));
586 while ((fd
= accept(netfd
, (struct sockaddr
*)&client
, &clientlen
)) >= 0) {
587 char host
[INET6_ADDRSTRLEN
];
589 if (hosts_ctl("p910nd", STRING_UNKNOWN
, get_ip_str((struct sockaddr
*)&client
, host
, sizeof(host
)), STRING_UNKNOWN
) == 0) {
591 "Connection from %s port %hu rejected\n", get_ip_str((struct sockaddr
*)&client
, host
, sizeof(host
)), get_port((struct sockaddr
*)&client
));
596 syslog(LOG_NOTICE
, "Connection from %s port %hu accepted\n", get_ip_str((struct sockaddr
*)&client
, host
, sizeof(host
)), get_port((struct sockaddr
*)&client
));
597 /*write(fd, "Printing", 8); */
599 /* Make sure lp device is open... */
600 while ((lp
= open_printer(lpnumber
)) == -1) {
602 if (open_sleep
< 320) /* ~5 min interval to avoid spam in syslog */
607 if (copy_stream(fd
, lp
) < 0)
608 syslog(LOGOPTS
, "copy_stream: %m\n");
612 syslog(LOGOPTS
, "accept: %m\n");
617 int is_standalone(void)
619 struct sockaddr_storage bind_addr
;
623 * Check to see if a socket was passed to us from (x)inetd.
625 * Use getsockname() to determine if descriptor 0 is indeed a socket
626 * (and thus we are probably a child of (x)inetd) or if it is instead
627 * something else and we are running standalone.
629 ba_len
= sizeof(bind_addr
);
630 if (getsockname(0, (struct sockaddr
*)&bind_addr
, &ba_len
) == 0)
631 return (0); /* under (x)inetd */
632 if (errno
!= ENOTSOCK
) /* strange... */
633 syslog(LOGOPTS
, "getsockname: %m\n");
637 int main(int argc
, char *argv
[])
642 if (argc
<= 0) /* in case not provided in (x)inetd config */
646 if ((p
= strrchr(progname
, '/')) != 0)
650 while ((c
= getopt(argc
, argv
, "bi:f:v")) != EOF
) {
672 if (isdigit(argv
[0][0]))
673 lpnumber
= argv
[0][0];
675 /* change the n in argv[0] to match the port so ps will show that */
676 if ((p
= strstr(progname
, "p910n")) != NULL
)
679 /* We used to pass (LOG_PERROR|LOG_PID|LOG_LPR|LOG_ERR) to syslog, but
680 * syslog ignored the LOG_PID and LOG_PERROR option. I.e. the intention
681 * was to add both options but the effect was to have neither.
682 * I disagree with the intention to add PERROR. --Stef */
683 openlog(p
, LOG_PID
, LOG_LPR
);