2 * $Id: pap.c,v 1.12 2009-10-14 01:38:28 didg Exp $
4 * Copyright (c) 1990,1994 Regents of The University of Michigan.
5 * All Rights Reserved. See COPYRIGHT.
10 #endif /* HAVE_CONFIG_H */
12 #include <sys/types.h>
15 #include <netatalk/endian.h>
16 #include <netatalk/at.h>
18 #include <atalk/atp.h>
19 #include <atalk/pap.h>
20 #include <atalk/nbp.h>
21 #include <atalk/util.h>
24 #endif /* HAVE_FCNTL_H */
32 #define _PATH_PAPRC ".paprc"
33 static char *nbpfailure = "AppleTalk printer offline";
35 /* Forward Declarations */
36 static void updatestatus(char *s, int len);
37 static int send_file(int fd, ATP atp, int lastfile);
39 static void usage(char *path)
43 if (( p = strrchr( path, '/' )) == NULL ) {
49 "Usage:\t%s [ -A address ] [ -c ] [ -d ] [ -e ] [ -E ] [ -p printer ]\n"
50 " [ -s statusfile ] [ -w ] [ -W ] [ FILES ]\n"
51 " -A address - printer Appletalk address\n"
52 " -c - take cuts (lie about wait time)\n"
53 " -d - enable debug\n"
54 " -e - send stdout to stderr\n"
55 " -E - don't wait for EOF from printer\n"
56 " -p printer - printer name\n"
57 " -s statusfile - put current printer status in statusfile\n"
58 " -w - wait for printer status = 'waiting'\n"
59 " -W - wait for printer status = 'idle'\n"
60 " FILES - send FILES to printer\n"
68 static char s[ 32 + 1 + 32 + 1 + 32 ];
72 if (( f = fopen( _PATH_PAPRC, "r" )) == NULL ) {
73 if ( errno == ENOENT ) {
76 perror( _PATH_PAPRC );
80 while ( fgets( s, sizeof( s ), f ) != NULL ) {
81 s[ strlen( s ) - 1 ] = '\0'; /* remove trailing newline */
92 static char *printer = NULL;
93 static char *status = NULL;
95 static int waitforprinter = 0;
97 static unsigned char connid, quantum, oquantum = PAP_MAXQUANTUM;
98 static struct sockaddr_at sat;
100 static char cbuf[ 8 ];
101 static struct nbpnve nn;
104 static char fbuf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
105 static struct iovec rfiov[ PAP_MAXQUANTUM ] = {
106 { fbuf[ 0 ] + 4, 0 },
107 { fbuf[ 1 ] + 4, 0 },
108 { fbuf[ 2 ] + 4, 0 },
109 { fbuf[ 3 ] + 4, 0 },
110 { fbuf[ 4 ] + 4, 0 },
111 { fbuf[ 5 ] + 4, 0 },
112 { fbuf[ 6 ] + 4, 0 },
113 { fbuf[ 7 ] + 4, 0 },
116 static struct iovec sniov[ PAP_MAXQUANTUM ] = {
127 static char nbuf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
128 static struct iovec rniov[ PAP_MAXQUANTUM ] = {
139 static struct iovec sfiov[ PAP_MAXQUANTUM ] = {
140 { nbuf[ 0 ] + 4, 0 },
141 { nbuf[ 1 ] + 4, 0 },
142 { nbuf[ 2 ] + 4, 0 },
143 { nbuf[ 3 ] + 4, 0 },
144 { nbuf[ 4 ] + 4, 0 },
145 { nbuf[ 5 ] + 4, 0 },
146 { nbuf[ 6 ] + 4, 0 },
147 { nbuf[ 7 ] + 4, 0 },
152 int main( int ac, char **av)
155 struct atp_block atpb;
156 int c, err = 0, fd, cuts = 0;
157 char *obj = NULL, *type = "LaserWriter", *zone = "*";
158 struct timeval stv, tv;
159 char rbuf[ ATP_MAXDATA ];
161 unsigned short waiting, result;
162 int connattempts = 10;
169 memset(&addr, 0, sizeof(addr));
170 while (( c = getopt( ac, av, "dWwcep:s:EA:" )) != EOF ) {
182 /* enable debugging */
191 case 'e' : /* send stdout to stderr */
208 if (!atalk_aton(optarg, &addr)) {
209 fprintf(stderr, "Bad address.\n");
221 if ( printer == NULL && (( printer = paprc()) == NULL )) {
222 fprintf( stderr, "No printer specified and ./.paprc not found.\n" );
229 if ( nbp_name( printer, &obj, &type, &zone ) < 0 ) {
230 fprintf( stderr, "%s: Bad name\n", printer );
234 fprintf( stderr, "%s: Bad name\n", printer );
238 if ( nbp_lookup( obj, type, zone, &nn, 1, &addr ) <= 0 ) {
240 perror( "nbp_lookup" );
243 fprintf( stderr, "%s:%s@%s: NBP Lookup failed\n", obj, type, zone );
248 printf( "Trying %u.%d:%d ...\n", ntohs( nn.nn_sat.sat_addr.s_net ),
249 nn.nn_sat.sat_addr.s_node, nn.nn_sat.sat_port );
252 if (( atp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
253 perror( "atp_open" );
256 if (( satp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
257 perror( "atp_open" );
261 while ( waitforidle ) {
262 char st_buf[ 1024 ]; /* XXX too big */
265 cbuf[ 1 ] = PAP_SENDSTATUS;
266 cbuf[ 2 ] = cbuf[ 3 ] = 0;
267 atpb.atp_saddr = &nn.nn_sat;
268 atpb.atp_sreqdata = cbuf;
269 atpb.atp_sreqdlen = 4; /* bytes in SendStatus request */
270 atpb.atp_sreqto = 2; /* retry timer */
271 atpb.atp_sreqtries = 5; /* retry count */
272 if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
273 perror( "atp_sreq" );
277 if(debug){ printf( "SENDSTATUS >\n" ), fflush( stdout );}
279 atpb.atp_saddr = &nn.nn_sat;
280 rniov[ 0 ].iov_len = PAP_MAXDATA + 4;
281 atpb.atp_rresiov = rniov;
282 atpb.atp_rresiovcnt = 1;
283 if ( atp_rresp( satp, &atpb ) < 0 ) {
284 perror( "atp_rresp" );
288 #ifndef NONZEROSTATUS
290 * The stinking LaserWriter IINTX puts crap in this
293 if ( ((char *)rniov[ 0 ].iov_base)[ 0 ] != 0 ) {
294 fprintf( stderr, "Bad status response!\n" );
297 #endif /* NONZEROSTATUS */
299 if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_STATUS ||
300 atpb.atp_rresiovcnt != 1 ) {
301 fprintf( stderr, "Bad status response!\n" );
305 if(debug){ printf( "< STATUS\n" ), fflush( stdout );}
307 memcpy( st_buf, (char *) rniov[ 0 ].iov_base + 9,
308 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
309 st_buf[ (int) ((char *)rniov[ 0 ].iov_base)[ 8 ]] = '\0';
310 if ( strstr( st_buf, "idle" ) != NULL ) {
313 updatestatus( (char *) rniov[ 0 ].iov_base + 9,
314 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
319 cbuf[ 0 ] = connid = getpid() & 0xff;
320 cbuf[ 1 ] = PAP_OPEN;
321 cbuf[ 2 ] = cbuf[ 3 ] = 0;
322 cbuf[ 4 ] = atp_sockaddr( atp )->sat_port;
323 cbuf[ 5 ] = oquantum; /* flow quantum */
324 if ( gettimeofday( &stv, 0 ) < 0 ) {
325 perror( "gettimeofday" );
332 if ( gettimeofday( &tv, 0 ) < 0 ) {
333 perror( "gettimeofday" );
336 waiting = htons( tv.tv_sec - stv.tv_sec );
338 memcpy(cbuf + 6, &waiting, sizeof( waiting ));
340 atpb.atp_saddr = &nn.nn_sat;
341 atpb.atp_sreqdata = cbuf;
342 atpb.atp_sreqdlen = 8; /* bytes in OpenConn request */
343 atpb.atp_sreqto = 2; /* retry timer */
344 atpb.atp_sreqtries = 5; /* retry count */
345 if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
346 perror( "atp_sreq" );
350 if(debug){ printf( "OPEN >\n" ), fflush( stdout );}
353 iov.iov_len = sizeof( rbuf );
354 atpb.atp_rresiov = &iov;
355 atpb.atp_rresiovcnt = 1;
356 if ( atp_rresp( atp, &atpb ) < 0 ) {
357 perror( "atp_rresp" );
358 if ( connattempts-- <= 0 ) {
359 fprintf( stderr, "Can't connect!\n" );
366 if ( iov.iov_len < 8 || (unsigned char)rbuf[ 0 ] != connid ||
367 rbuf[ 1 ] != PAP_OPENREPLY ) {
368 fprintf( stderr, "Bad response!\n" );
369 continue; /* This is weird, since TIDs must match... */
372 if(debug){ printf( "< OPENREPLY\n" ), fflush( stdout );}
375 printf( "%.*s\n", (int)iov.iov_len - 9, (char *) iov.iov_base + 9 );
377 updatestatus( (char *) iov.iov_base + 9, iov.iov_len - 9 );
379 memcpy( &result, rbuf + 6, sizeof( result ));
383 memcpy( &sat, &nn.nn_sat, sizeof( struct sockaddr_at ));
384 sat.sat_port = rbuf[ 4 ];
391 printf( "Connected to %.*s:%.*s@%.*s.\n",
392 nn.nn_objlen, nn.nn_obj,
393 nn.nn_typelen, nn.nn_type,
394 nn.nn_zonelen, nn.nn_zone );
397 if ( optind == ac ) {
398 send_file( 0, atp, 1 );
400 for (; optind < ac; optind++ ) {
401 if ( strcmp( av[ optind ], "-" ) == 0 ) {
403 } else if (( fd = open( av[ optind ], O_RDONLY )) < 0 ) {
404 perror( av[ optind ] );
407 send_file( fd, atp, ( optind == ac - 1 ) ? 1 : 0 );
418 cbuf[ 1 ] = PAP_CLOSE;
419 cbuf[ 2 ] = cbuf[ 3 ] = 0;
421 atpb.atp_saddr = &sat;
422 atpb.atp_sreqdata = cbuf;
423 atpb.atp_sreqdlen = 4; /* bytes in CloseConn request */
424 atpb.atp_sreqto = 2; /* retry timer */
425 atpb.atp_sreqtries = 5; /* retry count */
426 if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
427 perror( "atp_sreq" );
431 if(debug){ printf( "CLOSE >\n" ), fflush( stdout );}
434 iov.iov_len = sizeof( rbuf );
435 atpb.atp_rresiov = &iov;
436 atpb.atp_rresiovcnt = 1;
437 if ( atp_rresp( atp, &atpb ) < 0 ) {
438 perror( "atp_rresp" );
443 if ( iov.iov_len != 4 || rbuf[ 1 ] != PAP_CLOSEREPLY ) {
444 fprintf( stderr, "Bad response!\n" );
450 * The AGFA Viper Rip doesn't have the connection id in the close request.
452 if ((unsigned char)rbuf[ 0 ] != connid ) {
453 fprintf( stderr, "Bad connid in close!\n" );
456 #endif /* ZEROCONNID */
458 if(debug){ printf( "< CLOSEREPLY\n" ), fflush( stdout );}
461 printf( "Connection closed.\n" );
467 static unsigned char port;
468 static u_int16_t seq = 0, rseq = 1;
470 static int send_file( int fd, ATP atp, int lastfile)
472 struct timeval stv, tv;
473 struct sockaddr_at ssat;
474 struct atp_block atpb;
476 int fiovcnt = 0, eof = 0, senteof = 0, to = 0;
478 unsigned short netseq;
480 if ( gettimeofday( &stv, 0 ) < 0 ) {
481 perror( "gettimeofday" );
489 cbuf[ 1 ] = PAP_READ;
490 if ( ++seq == 0 ) seq = 1;
491 netseq = htons( seq );
492 memcpy( cbuf + 2, &netseq, sizeof( netseq ));
493 atpb.atp_saddr = &sat;
494 atpb.atp_sreqdata = cbuf;
495 atpb.atp_sreqdlen = 4; /* bytes in SendData request */
496 atpb.atp_sreqto = 15; /* retry timer */
497 atpb.atp_sreqtries = -1; /* retry count */
498 if ( atp_sreq( atp, &atpb, oquantum, ATP_XO ) < 0 ) {
499 perror( "atp_sreq" );
503 if(debug){ printf( "READ %d >\n", seq ), fflush( stdout );}
506 if ( gettimeofday( &tv, 0 ) < 0 ) {
507 perror( "gettimeofday" );
511 if (( tv.tv_sec - stv.tv_sec ) >= 60 ) {
518 cbuf[ 1 ] = PAP_TICKLE;
519 cbuf[ 2 ] = cbuf[ 3 ] = 0;
520 atpb.atp_saddr = &sat;
521 atpb.atp_sreqdata = cbuf;
522 atpb.atp_sreqdlen = 4; /* bytes in Tickle request */
523 atpb.atp_sreqto = 0; /* retry timer */
524 atpb.atp_sreqtries = 1; /* retry count */
525 if ( atp_sreq( satp, &atpb, 0, 0 ) < 0 ) {
526 perror( "atp_sreq" );
530 if(debug){ printf( "TICKLE >\n" ), fflush( stdout );}
533 tv.tv_sec = stv.tv_sec + 60 - tv.tv_sec;
537 if ( !waitforprinter && !eof && fiovcnt == 0 ) {
540 FD_SET( atp_fileno( atp ), &fds );
542 if (( cc = select( FD_SETSIZE, &fds, 0, 0, &tv )) < 0 ) {
548 * A timeout has occured. Keep track of it.
552 fprintf( stderr, "Connection timed out.\n" );
561 if ( !fiovcnt && FD_ISSET( fd, &fds )) {
562 for ( i = 0; i < quantum; i++ ) {
563 rfiov[ i ].iov_len = PAP_MAXDATA;
565 if (( cc = readv( fd, rfiov, quantum )) < 0 ) {
572 fiovcnt = cc / PAP_MAXDATA + ( cc % PAP_MAXDATA > 0 );
573 for ( i = 0; cc > 0; i++ ) {
574 rfiov[ i ].iov_len = ( cc > PAP_MAXDATA ) ? PAP_MAXDATA : cc;
575 cc -= ( cc > PAP_MAXDATA ) ? PAP_MAXDATA : cc;
579 if ( FD_ISSET( atp_fileno( atp ), &fds )) {
581 ssat.sat_port = ATADDR_ANYPORT;
582 switch( atp_rsel( atp, &ssat, ATP_TRESP | ATP_TREQ )) {
584 atpb.atp_saddr = &ssat;
585 atpb.atp_rreqdata = cbuf;
586 atpb.atp_rreqdlen = sizeof( cbuf );
587 if ( atp_rreq( atp, &atpb ) < 0 ) {
588 perror( "atp_rreq" );
592 if ( (unsigned char)cbuf[ 0 ] != connid ) {
596 /* reset timeout counter for all valid requests */
599 switch ( cbuf[ 1 ] ) {
601 memcpy( cbuf + 2, &netseq, sizeof( netseq ));
602 if(debug){ printf( "< READ %d\n", ntohs( netseq )), fflush( stdout );}
605 if ( rseq != ntohs( netseq )) {
606 if(debug){ printf( "| DUP %d\n", rseq ), fflush( stdout );}
609 if ( rseq++ == 0xffff ) rseq = 1;
614 port = ssat.sat_port;
619 if(debug){ printf( "< CLOSE\n" ), fflush( stdout );}
622 * Respond to the close request, and fail.
624 sniov[ 0 ].iov_len = 4;
625 ((char *)sniov[ 0 ].iov_base)[ 0 ] = connid;
626 ((char *)sniov[ 0 ].iov_base)[ 1 ] = PAP_CLOSEREPLY;
627 ((char *)sniov[ 0 ].iov_base)[ 2 ] =
628 ((char *)sniov[ 0 ].iov_base)[ 3 ] = 0;
629 atpb.atp_sresiov = sniov;
630 atpb.atp_sresiovcnt = 1;
631 if ( atp_sresp( atp, &atpb ) < 0 ) {
632 perror( "atp_sresp" );
636 if(debug){ printf( "CLOSEREPLY >\n" ), fflush( stdout );}
638 fprintf( stderr, "Connection closed by foreign host.\n" );
643 if(debug){ printf( "< TICKLE\n" ), fflush( stdout );}
647 fprintf( stderr, "Bad PAP request!\n" );
653 /* reset timeout counter for all valid requests */
656 atpb.atp_saddr = &ssat;
657 for ( i = 0; i < oquantum; i++ ) {
658 rniov[ i ].iov_len = PAP_MAXDATA + 4;
660 atpb.atp_rresiov = rniov;
661 atpb.atp_rresiovcnt = oquantum;
662 if ( atp_rresp( atp, &atpb ) < 0 ) {
663 perror( "atp_rresp" );
669 * The HP LJIIISI w/ BridgePort LocalTalk card sends
670 * zero instead of the connid.
672 if ( ((unsigned char *)rniov[ 0 ].iov_base)[ 0 ] != connid ) {
673 fprintf( stderr, "Bad data response!\n" );
676 #endif /* ZEROCONNID */
677 if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_DATA ) {
678 fprintf( stderr, "Bad data response!\n" );
682 for ( cc = 0, i = 0; i < atpb.atp_rresiovcnt; i++ ) {
683 sfiov[ i ].iov_len = rniov[ i ].iov_len - 4;
684 cc += sfiov[ i ].iov_len;
686 if ( cc && writev( 1, sfiov, atpb.atp_rresiovcnt ) < cc ) {
692 if ( ((char *)rniov[ 0 ].iov_base)[ 2 ] ) {
694 if(debug){ printf( "< DATA (eof)\n" ), fflush( stdout );}
699 if(debug){ printf( "< DATA\n" ), fflush( stdout );}
706 cbuf[ 1 ] = PAP_READ;
707 if ( ++seq == 0 ) seq = 1;
708 netseq = htons( seq );
709 memcpy( cbuf + 2, &netseq, sizeof( netseq ));
710 atpb.atp_saddr = &sat;
711 atpb.atp_sreqdata = cbuf;
712 atpb.atp_sreqdlen = 4; /* bytes in SendData request */
713 atpb.atp_sreqto = 15; /* retry timer */
714 atpb.atp_sreqtries = -1; /* retry count */
715 if ( atp_sreq( atp, &atpb, oquantum, ATP_XO ) < 0 ) {
716 perror( "atp_sreq" );
720 if(debug){ printf( "READ %d >\n", seq ), fflush( stdout );}
726 if(debug){ printf( "| RETRANS\n" ), fflush( stdout );}
731 perror( "atp_rsel" );
737 * Send whatever is pending.
739 if ( !waitforprinter && !senteof && data && ( fiovcnt || eof )) {
740 ssat.sat_port = port;
741 atpb.atp_saddr = &ssat;
743 for ( i = 0; i < fiovcnt; i++ ) {
744 sniov[ i ].iov_len = rfiov[ i ].iov_len + 4;
745 ((char *)sniov[ i ].iov_base)[ 0 ] = connid;
746 ((char *)sniov[ i ].iov_base)[ 1 ] = PAP_DATA;
747 senteof = ((char *)sniov[ i ].iov_base)[ 2 ] = eof;
748 ((char *)sniov[ i ].iov_base)[ 3 ] = 0;
751 sniov[ 0 ].iov_len = 4;
752 ((char *)sniov[ 0 ].iov_base)[ 0 ] = connid;
753 ((char *)sniov[ 0 ].iov_base)[ 1 ] = PAP_DATA;
754 senteof = ((char *)sniov[ 0 ].iov_base)[ 2 ] = eof;
755 ((char *)sniov[ 0 ].iov_base)[ 3 ] = 0;
757 atpb.atp_sresiov = sniov;
758 atpb.atp_sresiovcnt = fiovcnt ? fiovcnt : 1;
759 if ( atp_sresp( atp, &atpb ) < 0 ) {
760 perror( "atp_sresp" );
765 if(debug){ printf( "DATA %s\n", eof ? "(eof) >" : ">" ), fflush( stdout );}
768 * The Apple LaserWriter IIf, the HP LWIIISi, and IV, don't
769 * seem to send us an EOF on large jobs. To work around
770 * this heinous protocol violation, we won't wait for their
771 * EOF before closing.
773 if ( eof && noeof && lastfile ) {
778 * If we can't send data right now, go ahead and get the
779 * status. This is cool, because we get here reliably
780 * if there is a problem.
783 cbuf[ 1 ] = PAP_SENDSTATUS;
784 cbuf[ 2 ] = cbuf[ 3 ] = 0;
785 atpb.atp_saddr = &nn.nn_sat;
786 atpb.atp_sreqdata = cbuf;
787 atpb.atp_sreqdlen = 4; /* bytes in SendStatus request */
788 atpb.atp_sreqto = 2; /* retry timer */
789 atpb.atp_sreqtries = 5; /* retry count */
790 if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
791 perror( "atp_sreq" );
795 if(debug){ printf( "SENDSTATUS >\n" ), fflush( stdout );}
797 atpb.atp_saddr = &nn.nn_sat;
798 rniov[ 0 ].iov_len = PAP_MAXDATA + 4;
799 atpb.atp_rresiov = rniov;
800 atpb.atp_rresiovcnt = 1;
801 if ( atp_rresp( satp, &atpb ) < 0 ) {
802 perror( "atp_rresp" );
806 #ifndef NONZEROSTATUS
808 * The stinking LaserWriter IINTX puts crap in this
811 if ( ((char *)rniov[ 0 ].iov_base)[ 0 ] != 0 ) {
812 fprintf( stderr, "Bad status response!\n" );
815 #endif /* NONZEROSTATUS */
817 if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_STATUS ||
818 atpb.atp_rresiovcnt != 1 ) {
819 fprintf( stderr, "Bad status response!\n" );
823 if(debug){ printf( "< STATUS\n" ), fflush( stdout );}
826 if ( waitforprinter ) {
827 char st_buf[ 1024 ]; /* XXX too big */
829 memcpy( st_buf, (char *) rniov[ 0 ].iov_base + 9,
830 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
831 st_buf[ (int) ((char *)rniov[ 0 ].iov_base)[ 8 ]] = '\0';
832 if ( strstr( st_buf, "waiting" ) != NULL ) {
838 updatestatus( (char *) rniov[ 0 ].iov_base + 9,
839 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
844 static void updatestatus(char *s, int len)
847 struct iovec iov[ 3 ];
850 if (( fd = open( status, O_WRONLY|O_TRUNC )) < 0 ) {
860 iov[ 0 ].iov_base = "%%[ ";
861 iov[ 0 ].iov_len = 4;
862 iov[ 1 ].iov_base = s;
863 iov[ 1 ].iov_len = len;
864 iov[ 2 ].iov_base = " ]%%\n";
865 iov[ 2 ].iov_len = 5;
867 writev( fd, iov, 3 );