2
* $Id: lp.c,v 1.14 2002/09/29 23:29:13 sibaz 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
if ( flock( fd, LOCK_EX ) < 0 ) {
292
LOG(log_error, logtype_papd, "lp_init: can't lock .seq" );
293
spoolerror( out, NULL );
298
if (( len = read( fd, buf, sizeof( buf ))) < 0 ) {
299
LOG(log_error, logtype_papd, "lp_init read: %m" );
300
spoolerror( out, NULL );
304
for ( cp = buf; len; len--, cp++ ) {
305
if ( *cp < '0' || *cp > '9' ) {
308
n = n * 10 + ( *cp - '0' );
313
n = ( n + 1 ) % 1000;
314
sprintf( buf, "%03d\n", n );
316
write( fd, buf, strlen( buf ));
319
lp.lp_flags |= LP_PIPE;
320
lp.lp_seq = getpid();
323
lp.lp_flags |= LP_INIT;
327
int lp_open( out, sat )
329
struct sockaddr_at *sat;
331
char name[ MAXPATHLEN ];
333
struct passwd *pwent;
335
if (( lp.lp_flags & LP_INIT ) == 0 && lp_init( out, sat ) != 0 ) {
338
if ( lp.lp_flags & LP_OPEN ) {
339
LOG(log_error, logtype_papd, "lp_open already open" );
343
if ( lp.lp_flags & LP_PIPE ) {
344
/* go right to program */
345
if (lp.lp_person != NULL) {
346
if((pwent = getpwnam(lp.lp_person)) != NULL) {
347
if(setreuid(pwent->pw_uid, pwent->pw_uid) != 0) {
348
LOG(log_info, logtype_papd, "setreuid error: %m");
351
LOG(log_info, logtype_papd, "Error getting username (%s)", lp.lp_person);
354
if (( lp.lp_stream = popen( printer->p_printer, "w" )) == NULL ) {
355
LOG(log_error, logtype_papd, "lp_open popen %s: %m", printer->p_printer );
356
spoolerror( out, NULL );
360
sprintf( name, "df%c%03d%s", lp.lp_letter++, lp.lp_seq, hostname );
362
if (( fd = open( name, O_WRONLY|O_CREAT|O_EXCL, 0660 )) < 0 ) {
363
LOG(log_error, logtype_papd, "lp_open %s: %m", name );
364
spoolerror( out, NULL );
368
if (lp.lp_person != NULL) {
369
if ((pwent = getpwnam(lp.lp_person)) == NULL) {
370
LOG(log_error, logtype_papd, "getpwnam %s: no such user", lp.lp_person);
371
spoolerror( out, NULL );
375
if ((pwent = getpwnam(printer->p_operator)) == NULL) {
376
LOG(log_error, logtype_papd, "getpwnam %s: no such user", printer->p_operator);
377
spoolerror( out, NULL );
382
if (fchown(fd, pwent->pw_uid, -1) < 0) {
383
LOG(log_error, logtype_papd, "chown %s %s: %m", pwent->pw_name, name);
384
spoolerror( out, NULL );
388
if (( lp.lp_stream = fdopen( fd, "w" )) == NULL ) {
389
LOG(log_error, logtype_papd, "lp_open fdopen: %m" );
390
spoolerror( out, NULL );
394
lp.lp_flags |= LP_OPEN;
401
if (( lp.lp_flags & LP_INIT ) == 0 || ( lp.lp_flags & LP_OPEN ) == 0 ) {
404
fclose( lp.lp_stream );
406
lp.lp_flags &= ~LP_OPEN;
410
int lp_write( buf, len )
414
if (( lp.lp_flags & LP_OPEN ) == 0 ) {
418
if ( fwrite( buf, 1, len, lp.lp_stream ) != len ) {
419
LOG(log_error, logtype_papd, "lp_write: %m" );
427
char name[ MAXPATHLEN ];
430
if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
434
if ( lp.lp_flags & LP_OPEN ) {
438
for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
439
sprintf( name, "df%c%03d%s", letter, lp.lp_seq, hostname );
440
if ( unlink( name ) < 0 ) {
441
LOG(log_error, logtype_papd, "lp_cancel unlink %s: %m", name );
449
* Create printcap control file, signal printer. Errors here should
450
* remove queue files.
456
char buf[ MAXPATHLEN ];
457
char tfname[ MAXPATHLEN ];
458
char cfname[ MAXPATHLEN ];
463
if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
468
if ( printer->p_flags & P_SPOOLED ) {
469
sprintf( tfname, "tfA%03d%s", lp.lp_seq, hostname );
470
if (( fd = open( tfname, O_WRONLY|O_EXCL|O_CREAT, 0660 )) < 0 ) {
471
LOG(log_error, logtype_papd, "lp_print %s: %m", tfname );
474
if (( cfile = fdopen( fd, "w" )) == NULL ) {
475
LOG(log_error, logtype_papd, "lp_print %s: %m", tfname );
478
fprintf( cfile, "H%s\n", hostname ); /* XXX lp_host? */
480
if ( lp.lp_person ) {
481
fprintf( cfile, "P%s\n", lp.lp_person );
483
fprintf( cfile, "P%s\n", printer->p_operator );
486
if ( lp.lp_job && *lp.lp_job ) {
487
fprintf( cfile, "J%s\n", lp.lp_job );
488
fprintf( cfile, "T%s\n", lp.lp_job );
490
fprintf( cfile, "JMac Job\n" );
491
fprintf( cfile, "TMac Job\n" );
494
fprintf( cfile, "C%s\n", hostname ); /* XXX lp_host? */
496
if ( lp.lp_person ) {
497
fprintf( cfile, "L%s\n", lp.lp_person );
499
fprintf( cfile, "L%s\n", printer->p_operator );
502
for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
503
fprintf( cfile, "fdf%c%03d%s\n", letter, lp.lp_seq, hostname );
504
fprintf( cfile, "Udf%c%03d%s\n", letter, lp.lp_seq, hostname );
507
if ( lp.lp_job && *lp.lp_job ) {
508
fprintf( cfile, "N%s\n", lp.lp_job );
510
fprintf( cfile, "NMac Job\n" );
514
sprintf( cfname, "cfA%03d%s", lp.lp_seq, hostname );
515
if ( link( tfname, cfname ) < 0 ) {
516
LOG(log_error, logtype_papd, "lp_print can't link %s to %s: %m", cfname,
522
if (( s = lp_conn_unix()) < 0 ) {
523
LOG(log_error, logtype_papd, "lp_print: lp_conn_unix: %m" );
527
sprintf( buf, "\1%s\n", printer->p_printer );
529
if ( write( s, buf, n ) != n ) {
530
LOG(log_error, logtype_papd, "lp_print write: %m" );
533
if ( read( s, buf, 1 ) != 1 ) {
534
LOG(log_error, logtype_papd, "lp_print read: %m" );
538
lp_disconn_unix( s );
540
if ( buf[ 0 ] != '\0' ) {
541
LOG(log_error, logtype_papd, "lp_print lpd said %c: %m", buf[ 0 ] );
545
LOG(log_info, logtype_papd, "lp_print queued" );
549
int lp_disconn_unix( fd )
551
return( close( fd ));
557
struct sockaddr_un saun;
559
if (( s = socket( AF_UNIX, SOCK_STREAM, 0 )) < 0 ) {
560
LOG(log_error, logtype_papd, "lp_conn_unix socket: %m" );
563
memset( &saun, 0, sizeof( struct sockaddr_un ));
564
saun.sun_family = AF_UNIX;
565
strcpy( saun.sun_path, _PATH_DEVPRINTER );
566
if ( connect( s, (struct sockaddr *)&saun,
567
strlen( saun.sun_path ) + 2 ) < 0 ) {
568
LOG(log_error, logtype_papd, "lp_conn_unix connect %s: %m", saun.sun_path );
576
int lp_disconn_inet( int fd )
578
return( close( fd ));
583
int privfd, port = IPPORT_RESERVED - 1;
584
struct sockaddr_in sin;
588
if (( sp = getservbyname( "printer", "tcp" )) == NULL ) {
589
LOG(log_error, logtype_papd, "printer/tcp: unknown service\n" );
593
if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
594
LOG(log_error, logtype_papd, "gethostname: %m" );
598
if (( hp = gethostbyname( hostname )) == NULL ) {
599
LOG(log_error, logtype_papd, "%s: unknown host\n", hostname );
603
if (( privfd = rresvport( &port )) < 0 ) {
604
LOG(log_error, logtype_papd, "lp_connect: socket: %m" );
609
memset( &sin, 0, sizeof( struct sockaddr_in ));
610
sin.sin_family = AF_INET;
611
/* sin.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); */
612
memcpy( &sin.sin_addr, hp->h_addr, hp->h_length );
613
sin.sin_port = sp->s_port;
615
if ( connect( privfd, (struct sockaddr *)&sin,
616
sizeof( struct sockaddr_in )) < 0 ) {
617
LOG(log_error, logtype_papd, "lp_connect: %m" );
631
if (( s = lp_conn_inet()) < 0 ) {
632
LOG(log_error, logtype_papd, "lp_rmjob: %m" );
636
if ( lp.lp_person == NULL ) {
640
sprintf( buf, "\5%s %s %d\n", printer->p_printer, lp.lp_person, job );
642
if ( write( s, buf, n ) != n ) {
643
LOG(log_error, logtype_papd, "lp_rmjob write: %m" );
644
lp_disconn_inet( s );
647
while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
648
LOG(log_debug, logtype_papd, "read %.*s", n, buf );
651
lp_disconn_inet( s );
655
char *kw_rank = "Rank";
656
char *kw_active = "active";
658
char *tag_rank = "rank: ";
659
char *tag_owner = "owner: ";
660
char *tag_job = "job: ";
661
char *tag_files = "files: ";
662
char *tag_size = "size: ";
663
char *tag_status = "status: ";
668
char buf[ 1024 ], *start, *stop, *p, *q;
669
int linelength, crlflength;
670
static struct papfile pf;
673
if (( s = lp_conn_unix()) < 0 ) {
674
LOG(log_error, logtype_papd, "lp_queue: %m" );
678
sprintf( buf, "\3%s\n", printer->p_printer );
680
if ( write( s, buf, n ) != n ) {
681
LOG(log_error, logtype_papd, "lp_queue write: %m" );
682
lp_disconn_unix( s );
685
pf.pf_state = PF_BOT;
687
while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
688
append( &pf, buf, n );
692
if ( markline( &pf, &start, &linelength, &crlflength ) > 0 ) {
694
stop = start + linelength;
695
for ( p = start; p < stop; p++ ) {
696
if ( *p == ' ' || *p == '\t' ) {
701
CONSUME( &pf , linelength + crlflength);
706
* Keys: "Rank", a number, "active"
707
* Anything else is status.
710
if ( len == strlen( kw_rank ) &&
711
strncmp( kw_rank, start, len ) == 0 ) {
712
CONSUME( &pf, linelength + crlflength );
715
if (( len == strlen( kw_active ) &&
716
strncmp( kw_active, start, len ) == 0 ) ||
717
isdigit( *start )) { /* a job line */
718
append( out, tag_rank, strlen( tag_rank ));
719
append( out, start, p - start );
720
append( out, "\n", 1 );
722
for ( ; p < stop; p++ ) {
723
if ( *p != ' ' && *p != '\t' ) {
727
for ( q = p; p < stop; p++ ) {
728
if ( *p == ' ' || *p == '\t' ) {
733
append( out, ".\n", 2 );
734
CONSUME( &pf, linelength + crlflength );
737
append( out, tag_owner, strlen( tag_owner ));
738
append( out, q, p - q );
739
append( out, "\n", 1 );
741
for ( ; p < stop; p++ ) {
742
if ( *p != ' ' && *p != '\t' ) {
746
for ( q = p; p < stop; p++ ) {
747
if ( *p == ' ' || *p == '\t' ) {
752
append( out, ".\n", 2 );
753
CONSUME( &pf , linelength + crlflength );
756
append( out, tag_job, strlen( tag_job ));
757
append( out, q, p - q );
758
append( out, "\n", 1 );
760
for ( ; p < stop; p++ ) {
761
if ( *p != ' ' && *p != '\t' ) {
765
for ( q = p, p = stop; p > q; p-- ) {
766
if ( *p == ' ' || *p == '\t' ) {
770
for ( ; p > q; p-- ) {
771
if ( *p != ' ' && *p != '\t' ) {
775
for ( ; p > q; p-- ) {
776
if ( *p == ' ' || *p == '\t' ) {
781
append( out, ".\n", 2 );
782
CONSUME( &pf, linelength + crlflength );
785
append( out, tag_files, strlen( tag_files ));
786
append( out, q, p - q );
787
append( out, "\n", 1 );
789
for ( ; p < stop; p++ ) {
790
if ( *p != ' ' && *p != '\t' ) {
794
append( out, tag_size, strlen( tag_size ));
795
append( out, p, stop - p );
796
append( out, "\n.\n", 3 );
798
CONSUME( &pf, linelength + crlflength );
803
append( out, tag_status, strlen( tag_status ));
804
append( out, start, linelength );
805
append( out, "\n.\n", 3 );
807
CONSUME( &pf, linelength + crlflength );
809
append( out, "*\n", 2 );
810
lp_disconn_unix( s );