2 * $Id: lp.c,v 1.14.8.1 2004-02-20 20:53:14 bfernhomberg Exp $
4 * Copyright (c) 1990,1994 Regents of The University of Michigan.
5 * All Rights Reserved. See COPYRIGHT.
8 * Copyright (c) 1983 Regents of the University of California.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 * Interface to lpr system.
46 #endif /* HAVE_CONFIG_H */
48 #include <sys/param.h>
49 #include <atalk/logger.h>
51 #include <sys/socket.h>
56 #endif /* HAVE_UNISTD_H */
58 #if defined( sun ) && defined( __svr4__ )
59 #include </usr/ucbinclude/sys/file.h>
60 #else /* sun && __svr4__ */
62 #endif /* sun && __svr4__ */
64 #include <netinet/in.h>
66 #include <netatalk/at.h>
67 #include <atalk/atp.h>
68 #include <atalk/paths.h>
72 #endif /* ABS_PRINT */
80 #endif /* HAVE_FCNTL_H */
87 /* These functions aren't used outside of lp.c */
89 int lp_disconn_inet( int );
91 int lp_disconn_unix( int );
93 char hostname[ MAXHOSTNAMELEN ];
95 extern struct sockaddr_at *sat;
106 #define LP_INIT (1<<0)
107 #define LP_OPEN (1<<1)
108 #define LP_PIPE (1<<2)
109 #define LP_CONNECT (1<<3)
110 #define LP_QUEUE (1<<4)
112 void lp_person( person )
115 if ( lp.lp_person != NULL ) {
116 free( lp.lp_person );
118 if (( lp.lp_person = (char *)malloc( strlen( person ) + 1 )) == NULL ) {
119 LOG(log_error, logtype_papd, "malloc: %m" );
122 strcpy( lp.lp_person, person );
132 if ( lp.lp_person == NULL ) {
135 err = ABS_canprint( lp.lp_person, printer->p_role, printer->p_srvid,
137 printer->p_pagecost = floor( atof( cost ) * 10000.0 );
138 printer->p_balance = atof( balance ) + atof( cost );
139 return( err < 0 ? -1 : 0 );
141 #endif /* ABS_PRINT */
146 if ( lp.lp_host != NULL ) {
149 if (( lp.lp_host = (char *)malloc( strlen( host ) + 1 )) == NULL ) {
150 LOG(log_error, logtype_papd, "malloc: %m" );
153 strcpy( lp.lp_host, host );
161 if ( lp.lp_job != NULL ) {
164 if (( lp.lp_job = (char *)malloc( strlen( job ) + 1 )) == NULL ) {
165 LOG(log_error, logtype_papd, "malloc: %m" );
168 for ( p = job, q = lp.lp_job; *p != '\0'; p++, q++ ) {
169 if ( !isascii( *p ) || !isprint( *p ) || *p == '\\' ) {
178 int lp_init( out, sat )
180 struct sockaddr_at *sat;
183 char *cp, buf[ BUFSIZ ];
185 int authenticated = 0;
189 #endif /* ABS_PRINT */
191 if ( printer->p_flags & P_AUTH ) {
194 /* cap style "log on to afp server before printing" authentication */
196 if ( printer->p_authprintdir && (printer->p_flags & P_AUTH_CAP) ) {
197 int addr_net = ntohs( sat->sat_addr.s_net );
198 int addr_node = sat->sat_addr.s_node;
199 char addr_filename[256];
200 char auth_string[256];
201 char *username, *afpdpid;
205 memset( auth_string, 0, 256 );
206 sprintf(addr_filename, "%s/net%d.%dnode%d",
207 printer->p_authprintdir, addr_net/256, addr_net%256,
209 if (stat(addr_filename, &cap_st) == 0) {
210 if ((cap_file = fopen(addr_filename, "r")) != NULL) {
211 if (fgets(auth_string, 256, cap_file) != NULL) {
212 username = auth_string;
213 if ((afpdpid = strrchr( auth_string, ':' )) != NULL) {
217 if (getpwnam(username) != NULL ) {
218 LOG(log_info, logtype_papd, "CAP authenticated %s", username);
222 LOG(log_info, logtype_papd, "CAP error: invalid username: '%s'", username);
225 LOG(log_info, logtype_papd, "CAP error: could not read username");
228 LOG(log_info, logtype_papd, "CAP error: %m");
231 LOG(log_info, logtype_papd, "CAP error: %m");
235 if ( printer->p_flags & P_AUTH_PSSP ) {
236 if ( lp.lp_person != NULL ) {
241 if ( authenticated == 0 ) {
242 LOG(log_error, logtype_papd, "lp_init: must authenticate" );
243 spoolerror( out, "Authentication required." );
248 if (( printer->p_flags & P_ACCOUNT ) && printer->p_pagecost > 0 &&
249 ! ABS_canprint( lp.lp_person, printer->p_role,
250 printer->p_srvid, cost, balance )) {
251 LOG(log_error, logtype_papd, "lp_init: no ABS funds" );
252 spoolerror( out, "No ABS funds available." );
255 #endif /* ABS_PRINT */
258 if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
259 LOG(log_error, logtype_papd, "gethostname: %m" );
263 if ( lp.lp_flags & LP_INIT ) {
264 LOG(log_error, logtype_papd, "lp_init: already inited, die!" );
272 if ( printer->p_flags & P_SPOOLED ) {
273 /* check if queuing is enabled: mode & 010 on lock file */
274 if ( stat( printer->p_lock, &st ) < 0 ) {
275 LOG(log_error, logtype_papd, "lp_init: %s: %m", printer->p_lock );
276 spoolerror( out, NULL );
279 if ( st.st_mode & 010 ) {
280 LOG(log_info, logtype_papd, "lp_init: queuing is disabled" );
281 spoolerror( out, "Queuing is disabled." );
285 if (( fd = open( ".seq", O_RDWR|O_CREAT, 0661 )) < 0 ) {
286 LOG(log_error, logtype_papd, "lp_init: can't create .seq" );
287 spoolerror( out, NULL );
291 #ifndef SOLARIS /* flock is unsupported, I doubt this stuff work anyway with newer solaris so ignore for now */
292 if ( flock( fd, LOCK_EX ) < 0 ) {
293 LOG(log_error, logtype_papd, "lp_init: can't lock .seq" );
294 spoolerror( out, NULL );
300 if (( len = read( fd, buf, sizeof( buf ))) < 0 ) {
301 LOG(log_error, logtype_papd, "lp_init read: %m" );
302 spoolerror( out, NULL );
306 for ( cp = buf; len; len--, cp++ ) {
307 if ( *cp < '0' || *cp > '9' ) {
310 n = n * 10 + ( *cp - '0' );
315 n = ( n + 1 ) % 1000;
316 sprintf( buf, "%03d\n", n );
318 write( fd, buf, strlen( buf ));
321 lp.lp_flags |= LP_PIPE;
322 lp.lp_seq = getpid();
325 lp.lp_flags |= LP_INIT;
329 int lp_open( out, sat )
331 struct sockaddr_at *sat;
333 char name[ MAXPATHLEN ];
335 struct passwd *pwent;
337 if (( lp.lp_flags & LP_INIT ) == 0 && lp_init( out, sat ) != 0 ) {
340 if ( lp.lp_flags & LP_OPEN ) {
341 LOG(log_error, logtype_papd, "lp_open already open" );
345 if ( lp.lp_flags & LP_PIPE ) {
346 /* go right to program */
347 if (lp.lp_person != NULL) {
348 if((pwent = getpwnam(lp.lp_person)) != NULL) {
349 if(setreuid(pwent->pw_uid, pwent->pw_uid) != 0) {
350 LOG(log_info, logtype_papd, "setreuid error: %m");
353 LOG(log_info, logtype_papd, "Error getting username (%s)", lp.lp_person);
356 if (( lp.lp_stream = popen( printer->p_printer, "w" )) == NULL ) {
357 LOG(log_error, logtype_papd, "lp_open popen %s: %m", printer->p_printer );
358 spoolerror( out, NULL );
362 sprintf( name, "df%c%03d%s", lp.lp_letter++, lp.lp_seq, hostname );
364 if (( fd = open( name, O_WRONLY|O_CREAT|O_EXCL, 0660 )) < 0 ) {
365 LOG(log_error, logtype_papd, "lp_open %s: %m", name );
366 spoolerror( out, NULL );
370 if (lp.lp_person != NULL) {
371 if ((pwent = getpwnam(lp.lp_person)) == NULL) {
372 LOG(log_error, logtype_papd, "getpwnam %s: no such user", lp.lp_person);
373 spoolerror( out, NULL );
377 if ((pwent = getpwnam(printer->p_operator)) == NULL) {
378 LOG(log_error, logtype_papd, "getpwnam %s: no such user", printer->p_operator);
379 spoolerror( out, NULL );
384 if (fchown(fd, pwent->pw_uid, -1) < 0) {
385 LOG(log_error, logtype_papd, "chown %s %s: %m", pwent->pw_name, name);
386 spoolerror( out, NULL );
390 if (( lp.lp_stream = fdopen( fd, "w" )) == NULL ) {
391 LOG(log_error, logtype_papd, "lp_open fdopen: %m" );
392 spoolerror( out, NULL );
396 lp.lp_flags |= LP_OPEN;
403 if (( lp.lp_flags & LP_INIT ) == 0 || ( lp.lp_flags & LP_OPEN ) == 0 ) {
406 fclose( lp.lp_stream );
408 lp.lp_flags &= ~LP_OPEN;
412 int lp_write( buf, len )
416 if (( lp.lp_flags & LP_OPEN ) == 0 ) {
420 if ( fwrite( buf, 1, len, lp.lp_stream ) != len ) {
421 LOG(log_error, logtype_papd, "lp_write: %m" );
429 char name[ MAXPATHLEN ];
432 if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
436 if ( lp.lp_flags & LP_OPEN ) {
440 for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
441 sprintf( name, "df%c%03d%s", letter, lp.lp_seq, hostname );
442 if ( unlink( name ) < 0 ) {
443 LOG(log_error, logtype_papd, "lp_cancel unlink %s: %m", name );
451 * Create printcap control file, signal printer. Errors here should
452 * remove queue files.
458 char buf[ MAXPATHLEN ];
459 char tfname[ MAXPATHLEN ];
460 char cfname[ MAXPATHLEN ];
465 if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
470 if ( printer->p_flags & P_SPOOLED ) {
471 sprintf( tfname, "tfA%03d%s", lp.lp_seq, hostname );
472 if (( fd = open( tfname, O_WRONLY|O_EXCL|O_CREAT, 0660 )) < 0 ) {
473 LOG(log_error, logtype_papd, "lp_print %s: %m", tfname );
476 if (( cfile = fdopen( fd, "w" )) == NULL ) {
477 LOG(log_error, logtype_papd, "lp_print %s: %m", tfname );
480 fprintf( cfile, "H%s\n", hostname ); /* XXX lp_host? */
482 if ( lp.lp_person ) {
483 fprintf( cfile, "P%s\n", lp.lp_person );
485 fprintf( cfile, "P%s\n", printer->p_operator );
488 if ( lp.lp_job && *lp.lp_job ) {
489 fprintf( cfile, "J%s\n", lp.lp_job );
490 fprintf( cfile, "T%s\n", lp.lp_job );
492 fprintf( cfile, "JMac Job\n" );
493 fprintf( cfile, "TMac Job\n" );
496 fprintf( cfile, "C%s\n", hostname ); /* XXX lp_host? */
498 if ( lp.lp_person ) {
499 fprintf( cfile, "L%s\n", lp.lp_person );
501 fprintf( cfile, "L%s\n", printer->p_operator );
504 for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
505 fprintf( cfile, "fdf%c%03d%s\n", letter, lp.lp_seq, hostname );
506 fprintf( cfile, "Udf%c%03d%s\n", letter, lp.lp_seq, hostname );
509 if ( lp.lp_job && *lp.lp_job ) {
510 fprintf( cfile, "N%s\n", lp.lp_job );
512 fprintf( cfile, "NMac Job\n" );
516 sprintf( cfname, "cfA%03d%s", lp.lp_seq, hostname );
517 if ( link( tfname, cfname ) < 0 ) {
518 LOG(log_error, logtype_papd, "lp_print can't link %s to %s: %m", cfname,
524 if (( s = lp_conn_unix()) < 0 ) {
525 LOG(log_error, logtype_papd, "lp_print: lp_conn_unix: %m" );
529 sprintf( buf, "\1%s\n", printer->p_printer );
531 if ( write( s, buf, n ) != n ) {
532 LOG(log_error, logtype_papd, "lp_print write: %m" );
535 if ( read( s, buf, 1 ) != 1 ) {
536 LOG(log_error, logtype_papd, "lp_print read: %m" );
540 lp_disconn_unix( s );
542 if ( buf[ 0 ] != '\0' ) {
543 LOG(log_error, logtype_papd, "lp_print lpd said %c: %m", buf[ 0 ] );
547 LOG(log_info, logtype_papd, "lp_print queued" );
551 int lp_disconn_unix( fd )
553 return( close( fd ));
559 struct sockaddr_un saun;
561 if (( s = socket( AF_UNIX, SOCK_STREAM, 0 )) < 0 ) {
562 LOG(log_error, logtype_papd, "lp_conn_unix socket: %m" );
565 memset( &saun, 0, sizeof( struct sockaddr_un ));
566 saun.sun_family = AF_UNIX;
567 strcpy( saun.sun_path, _PATH_DEVPRINTER );
568 if ( connect( s, (struct sockaddr *)&saun,
569 strlen( saun.sun_path ) + 2 ) < 0 ) {
570 LOG(log_error, logtype_papd, "lp_conn_unix connect %s: %m", saun.sun_path );
578 int lp_disconn_inet( int fd )
580 return( close( fd ));
585 int privfd, port = IPPORT_RESERVED - 1;
586 struct sockaddr_in sin;
590 if (( sp = getservbyname( "printer", "tcp" )) == NULL ) {
591 LOG(log_error, logtype_papd, "printer/tcp: unknown service\n" );
595 if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
596 LOG(log_error, logtype_papd, "gethostname: %m" );
600 if (( hp = gethostbyname( hostname )) == NULL ) {
601 LOG(log_error, logtype_papd, "%s: unknown host\n", hostname );
605 if (( privfd = rresvport( &port )) < 0 ) {
606 LOG(log_error, logtype_papd, "lp_connect: socket: %m" );
611 memset( &sin, 0, sizeof( struct sockaddr_in ));
612 sin.sin_family = AF_INET;
613 /* sin.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); */
614 memcpy( &sin.sin_addr, hp->h_addr, hp->h_length );
615 sin.sin_port = sp->s_port;
617 if ( connect( privfd, (struct sockaddr *)&sin,
618 sizeof( struct sockaddr_in )) < 0 ) {
619 LOG(log_error, logtype_papd, "lp_connect: %m" );
633 if (( s = lp_conn_inet()) < 0 ) {
634 LOG(log_error, logtype_papd, "lp_rmjob: %m" );
638 if ( lp.lp_person == NULL ) {
642 sprintf( buf, "\5%s %s %d\n", printer->p_printer, lp.lp_person, job );
644 if ( write( s, buf, n ) != n ) {
645 LOG(log_error, logtype_papd, "lp_rmjob write: %m" );
646 lp_disconn_inet( s );
649 while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
650 LOG(log_debug, logtype_papd, "read %.*s", n, buf );
653 lp_disconn_inet( s );
657 char *kw_rank = "Rank";
658 char *kw_active = "active";
660 char *tag_rank = "rank: ";
661 char *tag_owner = "owner: ";
662 char *tag_job = "job: ";
663 char *tag_files = "files: ";
664 char *tag_size = "size: ";
665 char *tag_status = "status: ";
670 char buf[ 1024 ], *start, *stop, *p, *q;
671 int linelength, crlflength;
672 static struct papfile pf;
675 if (( s = lp_conn_unix()) < 0 ) {
676 LOG(log_error, logtype_papd, "lp_queue: %m" );
680 sprintf( buf, "\3%s\n", printer->p_printer );
682 if ( write( s, buf, n ) != n ) {
683 LOG(log_error, logtype_papd, "lp_queue write: %m" );
684 lp_disconn_unix( s );
687 pf.pf_state = PF_BOT;
689 while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
690 append( &pf, buf, n );
694 if ( markline( &pf, &start, &linelength, &crlflength ) > 0 ) {
696 stop = start + linelength;
697 for ( p = start; p < stop; p++ ) {
698 if ( *p == ' ' || *p == '\t' ) {
703 CONSUME( &pf , linelength + crlflength);
708 * Keys: "Rank", a number, "active"
709 * Anything else is status.
712 if ( len == strlen( kw_rank ) &&
713 strncmp( kw_rank, start, len ) == 0 ) {
714 CONSUME( &pf, linelength + crlflength );
717 if (( len == strlen( kw_active ) &&
718 strncmp( kw_active, start, len ) == 0 ) ||
719 isdigit( *start )) { /* a job line */
720 append( out, tag_rank, strlen( tag_rank ));
721 append( out, start, p - start );
722 append( out, "\n", 1 );
724 for ( ; p < stop; p++ ) {
725 if ( *p != ' ' && *p != '\t' ) {
729 for ( q = p; p < stop; p++ ) {
730 if ( *p == ' ' || *p == '\t' ) {
735 append( out, ".\n", 2 );
736 CONSUME( &pf, linelength + crlflength );
739 append( out, tag_owner, strlen( tag_owner ));
740 append( out, q, p - q );
741 append( out, "\n", 1 );
743 for ( ; p < stop; p++ ) {
744 if ( *p != ' ' && *p != '\t' ) {
748 for ( q = p; p < stop; p++ ) {
749 if ( *p == ' ' || *p == '\t' ) {
754 append( out, ".\n", 2 );
755 CONSUME( &pf , linelength + crlflength );
758 append( out, tag_job, strlen( tag_job ));
759 append( out, q, p - q );
760 append( out, "\n", 1 );
762 for ( ; p < stop; p++ ) {
763 if ( *p != ' ' && *p != '\t' ) {
767 for ( q = p, p = stop; p > q; p-- ) {
768 if ( *p == ' ' || *p == '\t' ) {
772 for ( ; p > q; p-- ) {
773 if ( *p != ' ' && *p != '\t' ) {
777 for ( ; p > q; p-- ) {
778 if ( *p == ' ' || *p == '\t' ) {
783 append( out, ".\n", 2 );
784 CONSUME( &pf, linelength + crlflength );
787 append( out, tag_files, strlen( tag_files ));
788 append( out, q, p - q );
789 append( out, "\n", 1 );
791 for ( ; p < stop; p++ ) {
792 if ( *p != ' ' && *p != '\t' ) {
796 append( out, tag_size, strlen( tag_size ));
797 append( out, p, stop - p );
798 append( out, "\n.\n", 3 );
800 CONSUME( &pf, linelength + crlflength );
805 append( out, tag_status, strlen( tag_status ));
806 append( out, start, linelength );
807 append( out, "\n.\n", 3 );
809 CONSUME( &pf, linelength + crlflength );
811 append( out, "*\n", 2 );
812 lp_disconn_unix( s );