]> arthur.barton.de Git - netatalk.git/blob - etc/papd/lp.c
be1e6c7171d1fb7ba32527d7ddfa8635f9c57508
[netatalk.git] / etc / papd / lp.c
1 /*
2  * Copyright (c) 1990,1994 Regents of The University of Michigan.
3  * All Rights Reserved.  See COPYRIGHT.
4  *
5  * Portions:
6  * Copyright (c) 1983 Regents of the University of California.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by the University of
20  *      California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37
38 /*
39  * Interface to lpr system.
40  */
41
42 #include <sys/param.h>
43 #include <sys/syslog.h>
44 #include <sys/time.h>
45 #include <sys/socket.h>
46 #include <sys/stat.h>
47 #if defined( sun ) && defined( __svr4__ )
48 #include </usr/ucbinclude/sys/file.h>
49 #else sun __svr4__
50 #include <sys/file.h>
51 #endif sun __svr4__
52 #include <sys/un.h>
53 #include <netinet/in.h>
54 #undef s_net
55 #include <netatalk/at.h>
56 #include <atalk/atp.h>
57 #include <atalk/paths.h>
58
59 #ifdef ABS_PRINT
60 #include <math.h>
61 #endif ABS_PRINT
62
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <netdb.h>
67 #include <fcntl.h>
68
69 #include "printer.h"
70 #include "file.h"
71
72 char    hostname[ MAXHOSTNAMELEN ];
73
74 /* initialize printing interface */
75 int     lp_init();
76 /* cancel current job */
77 int     lp_cancel();
78 /* print current job */
79 int     lp_print();
80
81 /* open a file for spooling */
82 int     lp_open();
83 /* open a buffer to the current open file */
84 int     lp_write();
85 /* close current spooling file */
86 int     lp_close();
87
88 struct lp {
89     int                 lp_flags;
90     FILE                *lp_stream;
91     int                 lp_seq;
92     char                lp_letter;
93     char                *lp_person;
94     char                *lp_host;
95     char                *lp_job;
96 } lp;
97 #define LP_INIT         (1<<0)
98 #define LP_OPEN         (1<<1)
99 #define LP_PIPE         (1<<2)
100 #define LP_CONNECT      (1<<3)
101 #define LP_QUEUE        (1<<4)
102
103 lp_person( person )
104     char        *person;
105 {
106     if ( lp.lp_person != NULL ) {
107         free( lp.lp_person );
108     }
109     if (( lp.lp_person = (char *)malloc( strlen( person ) + 1 )) == NULL ) {
110         syslog( LOG_ERR, "malloc: %m" );
111         exit( 1 );
112     }
113     strcpy( lp.lp_person, person );
114 }
115
116 #ifdef ABS_PRINT
117 lp_pagecost()
118 {
119     char        cost[ 22 ];
120     char        balance[ 22 ];
121     int         err;
122
123     if ( lp.lp_person == NULL ) {
124         return( -1 );
125     }
126     err = ABS_canprint( lp.lp_person, printer->p_role, printer->p_srvid,
127             cost, balance );
128     printer->p_pagecost = floor( atof( cost ) * 10000.0 );
129     printer->p_balance = atof( balance ) + atof( cost );
130     return( err < 0 ? -1 : 0 );
131 }
132 #endif ABS_PRINT
133
134 lp_host( host )
135     char        *host;
136 {
137     if ( lp.lp_host != NULL ) {
138         free( lp.lp_host );
139     }
140     if (( lp.lp_host = (char *)malloc( strlen( host ) + 1 )) == NULL ) {
141         syslog( LOG_ERR, "malloc: %m" );
142         exit( 1 );
143     }
144     strcpy( lp.lp_host, host );
145 }
146
147 lp_job( job )
148     char        *job;
149 {
150     char        *p, *q;
151
152     if ( lp.lp_job != NULL ) {
153         free( lp.lp_job );
154     }
155     if (( lp.lp_job = (char *)malloc( strlen( job ) + 1 )) == NULL ) {
156         syslog( LOG_ERR, "malloc: %m" );
157         exit( 1 );
158     }
159     for ( p = job, q = lp.lp_job; *p != '\0'; p++, q++ ) {
160         if ( !isascii( *p ) || !isprint( *p ) || *p == '\\' ) {
161             *q = '.';
162         } else {
163             *q = *p;
164         }
165     }
166     *q = '\0';
167 }
168
169 lp_init( out )
170     struct papfile      *out;
171 {
172     int         fd, n, len;
173     char        *cp, buf[ BUFSIZ ];
174     struct stat st;
175 #ifdef ABS_PRINT
176     char        cost[ 22 ];
177     char        balance[ 22 ];
178 #endif ABS_PRINT
179
180     if ( printer->p_flags & P_AUTH ) {
181         if ( lp.lp_person == NULL ) {
182             syslog( LOG_ERR, "lp_init: must authenticate" );
183             spoolerror( out, "Authentication required." );
184             return( -1 );
185         }
186 #ifdef ABS_PRINT
187         if (( printer->p_flags & P_ACCOUNT ) && printer->p_pagecost > 0 &&
188                 ! ABS_canprint( lp.lp_person, printer->p_role,
189                 printer->p_srvid, cost, balance )) {
190             syslog( LOG_ERR, "lp_init: no ABS funds" );
191             spoolerror( out, "No ABS funds available." );
192             return( -1 );
193         }
194 #endif ABS_PRINT
195     }
196
197     if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
198         syslog( LOG_ERR, "gethostname: %m" );
199         exit( 1 );
200     }
201
202     if ( lp.lp_flags & LP_INIT ) {
203         syslog( LOG_ERR, "lp_init: already inited, die!" );
204         abort();
205     }
206
207     lp.lp_flags = 0;
208     lp.lp_stream = NULL;
209     lp.lp_letter = 'A';
210
211     if ( printer->p_flags & P_SPOOLED ) {
212         /* check if queuing is enabled: mode & 010 on lock file */
213         if ( stat( printer->p_lock, &st ) < 0 ) {
214             syslog( LOG_ERR, "lp_init: %s: %m", printer->p_lock );
215             spoolerror( out, NULL );
216             return( -1 );
217         }
218         if ( st.st_mode & 010 ) {
219             syslog( LOG_INFO, "lp_init: queuing is disabled" );
220             spoolerror( out, "Queuing is disabled." );
221             return( -1 );
222         }
223
224         if (( fd = open( ".seq", O_RDWR|O_CREAT, 0661 )) < 0 ) {
225             syslog( LOG_ERR, "lp_init: can't create .seq" );
226             spoolerror( out, NULL );
227             return( -1 );
228         }
229
230         if ( flock( fd, LOCK_EX ) < 0 ) {
231             syslog( LOG_ERR, "lp_init: can't lock .seq" );
232             spoolerror( out, NULL );
233             return( -1 );
234         }
235
236         n = 0;
237         if (( len = read( fd, buf, sizeof( buf ))) < 0 ) {
238             syslog( LOG_ERR, "lp_init read: %m" );
239             spoolerror( out, NULL );
240             return( -1 );
241         }
242         if ( len > 0 ) {
243             for ( cp = buf; len; len--, cp++ ) {
244                 if ( *cp < '0' || *cp > '9' ) {
245                     break;
246                 }
247                 n = n * 10 + ( *cp - '0' );
248             }
249         }
250         lp.lp_seq = n;
251
252         n = ( n + 1 ) % 1000;
253         sprintf( buf, "%03d\n", n );
254         lseek( fd, 0L, 0 );
255         write( fd, buf, strlen( buf ));
256         close( fd );
257     } else {
258         lp.lp_flags |= LP_PIPE;
259         lp.lp_seq = getpid();
260     }
261
262     lp.lp_flags |= LP_INIT;
263     return( 0 );
264 }
265
266 lp_open( out )
267     struct papfile      *out;
268 {
269     char        name[ MAXPATHLEN ];
270     int         fd;
271
272     if (( lp.lp_flags & LP_INIT ) == 0 && lp_init( out ) != 0 ) {
273         return( -1 );
274     }
275     if ( lp.lp_flags & LP_OPEN ) {
276         syslog( LOG_ERR, "lp_open already open" );
277         abort();
278     }
279
280     if ( lp.lp_flags & LP_PIPE ) {
281         /* go right to program */
282         if (( lp.lp_stream = popen( printer->p_printer, "w" )) == NULL ) {
283             syslog( LOG_ERR, "lp_open popen %s: %m", printer->p_printer );
284             spoolerror( out, NULL );
285             return( -1 );
286         }
287     } else {
288         sprintf( name, "df%c%03d%s", lp.lp_letter++, lp.lp_seq, hostname );
289
290         if (( fd = open( name, O_WRONLY|O_CREAT|O_EXCL, 0660 )) < 0 ) {
291             syslog( LOG_ERR, "lp_open %s: %m", name );
292             spoolerror( out, NULL );
293             return( -1 );
294         }
295         if (( lp.lp_stream = fdopen( fd, "w" )) == NULL ) {
296             syslog( LOG_ERR, "lp_open fdopen: %m" );
297             spoolerror( out, NULL );
298             return( -1 );
299         }
300     }
301     lp.lp_flags |= LP_OPEN;
302
303     return( 0 );
304 }
305
306 lp_close()
307 {
308     if (( lp.lp_flags & LP_INIT ) == 0 || ( lp.lp_flags & LP_OPEN ) == 0 ) {
309         return;
310     }
311     fclose( lp.lp_stream );
312     lp.lp_stream = NULL;
313     lp.lp_flags &= ~LP_OPEN;
314     return;
315 }
316
317 lp_write( buf, len )
318     char        *buf;
319     int         len;
320 {
321     if (( lp.lp_flags & LP_OPEN ) == 0 ) {
322         return( -1 );
323     }
324
325     if ( fwrite( buf, 1, len, lp.lp_stream ) != len ) {
326         syslog( LOG_ERR, "lp_write: %m" );
327         abort();
328     }
329     return( 0 );
330 }
331
332 lp_cancel()
333 {
334     char        name[ MAXPATHLEN ];
335     char        letter;
336
337     if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
338         return;
339     }
340
341     if ( lp.lp_flags & LP_OPEN ) {
342         lp_close();
343     }
344
345     for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
346         sprintf( name, "df%c%03d%s", letter, lp.lp_seq, hostname );
347         if ( unlink( name ) < 0 ) {
348             syslog( LOG_ERR, "lp_cancel unlink %s: %m", name );
349         }
350     }
351
352     return;
353 }
354
355 /*
356  * Create printcap control file, signal printer.  Errors here should
357  * remove queue files.
358  *
359  * XXX piped?
360  */
361 lp_print()
362 {
363     char                buf[ MAXPATHLEN ];
364     char                tfname[ MAXPATHLEN ];
365     char                cfname[ MAXPATHLEN ];
366     char                letter;
367     int                 fd, n, s;
368     FILE                *cfile;
369
370     if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
371         return;
372     }
373     lp_close();
374
375     if ( printer->p_flags & P_SPOOLED ) {
376         sprintf( tfname, "tfA%03d%s", lp.lp_seq, hostname );
377         if (( fd = open( tfname, O_WRONLY|O_EXCL|O_CREAT, 0660 )) < 0 ) {
378             syslog( LOG_ERR, "lp_print %s: %m", tfname );
379             return;
380         }
381         if (( cfile = fdopen( fd, "w" )) == NULL ) {
382             syslog( LOG_ERR, "lp_print %s: %m", tfname );
383             return;
384         }
385         fprintf( cfile, "H%s\n", hostname );    /* XXX lp_host? */
386
387         if ( lp.lp_person ) {
388             fprintf( cfile, "P%s\n", lp.lp_person );
389         } else {
390             fprintf( cfile, "P%s\n", printer->p_operator );
391         }
392
393         if ( lp.lp_job && *lp.lp_job ) {
394             fprintf( cfile, "J%s\n", lp.lp_job );
395             fprintf( cfile, "T%s\n", lp.lp_job );
396         } else {
397             fprintf( cfile, "JMac Job\n" );
398             fprintf( cfile, "TMac Job\n" );
399         }
400
401         fprintf( cfile, "C%s\n", hostname );    /* XXX lp_host? */
402
403         if ( lp.lp_person ) {
404             fprintf( cfile, "L%s\n", lp.lp_person );
405         } else {
406             fprintf( cfile, "L%s\n", printer->p_operator );
407         }
408
409         for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
410             fprintf( cfile, "fdf%c%03d%s\n", letter, lp.lp_seq, hostname );
411             fprintf( cfile, "Udf%c%03d%s\n", letter, lp.lp_seq, hostname );
412         }
413
414         if ( lp.lp_job && *lp.lp_job ) {
415             fprintf( cfile, "N%s\n", lp.lp_job );
416         } else {
417             fprintf( cfile, "NMac Job\n" );
418         }
419         fclose( cfile );
420
421         sprintf( cfname, "cfA%03d%s", lp.lp_seq, hostname );
422         if ( link( tfname, cfname ) < 0 ) {
423             syslog( LOG_ERR, "lp_print can't link %s to %s: %m", cfname,
424                     tfname );
425             return;
426         }
427         unlink( tfname );
428
429         if (( s = lp_conn_unix()) < 0 ) {
430             syslog( LOG_ERR, "lp_print: lp_conn_unix: %m" );
431             return;
432         }
433
434         sprintf( buf, "\1%s\n", printer->p_printer );
435         n = strlen( buf );
436         if ( write( s, buf, n ) != n ) {
437             syslog( LOG_ERR, "lp_print write: %m" );
438             return;
439         }
440         if ( read( s, buf, 1 ) != 1 ) {
441             syslog( LOG_ERR, "lp_print read: %m" );
442             return;
443         }
444
445         lp_disconn_unix( s );
446
447         if ( buf[ 0 ] != '\0' ) {
448             syslog( LOG_ERR, "lp_print lpd said %c: %m", buf[ 0 ] );
449             return;
450         }
451     }
452     syslog( LOG_INFO, "lp_print queued" );
453     return;
454 }
455
456 lp_disconn_unix( fd )
457 {
458     return( close( fd ));
459 }
460
461 lp_conn_unix()
462 {
463     int                 s;
464     struct sockaddr_un  saun;
465
466     if (( s = socket( AF_UNIX, SOCK_STREAM, 0 )) < 0 ) {
467         syslog( LOG_ERR, "lp_conn_unix socket: %m" );
468         return( -1 );
469     }
470     bzero( &saun, sizeof( struct sockaddr_un ));
471     saun.sun_family = AF_UNIX;
472     strcpy( saun.sun_path, _PATH_DEVPRINTER );
473     if ( connect( s, (struct sockaddr *)&saun,
474             strlen( saun.sun_path ) + 2 ) < 0 ) {
475         syslog( LOG_ERR, "lp_conn_unix connect %s: %m", saun.sun_path );
476         close( s );
477         return( -1 );
478     }
479
480     return( s );
481 }
482
483 lp_disconn_inet( fd )
484 {
485     return( close( fd ));
486 }
487
488 lp_conn_inet()
489 {
490     int                 privfd, port = IPPORT_RESERVED - 1;
491     struct sockaddr_in  sin;
492     struct servent      *sp;
493     struct hostent      *hp;
494
495     if (( sp = getservbyname( "printer", "tcp" )) == NULL ) {
496         syslog( LOG_ERR, "printer/tcp: unknown service\n" );
497         return( -1 );
498     }
499
500     if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
501         syslog( LOG_ERR, "gethostname: %m" );
502         exit( 1 );
503     }
504
505     if (( hp = gethostbyname( hostname )) == NULL ) {
506         syslog( LOG_ERR, "%s: unknown host\n", hostname );
507         return( -1 );
508     }
509
510     if (( privfd = rresvport( &port )) < 0 ) {
511         syslog( LOG_ERR, "lp_connect: socket: %m" );
512         close( privfd );
513         return( -1 );
514     }
515
516     bzero( &sin, sizeof( struct sockaddr_in ));
517     sin.sin_family = AF_INET;
518 /*    sin.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); */
519     bcopy( hp->h_addr, &sin.sin_addr, hp->h_length );
520     sin.sin_port = sp->s_port;
521
522     if ( connect( privfd, (struct sockaddr *)&sin,
523             sizeof( struct sockaddr_in )) < 0 ) {
524         syslog( LOG_ERR, "lp_connect: %m" );
525         close( privfd );
526         return( -1 );
527     }
528
529     return( privfd );
530 }
531
532 lp_rmjob( job )
533     int         job;
534 {
535     char        buf[ 1024 ];
536     int         n, s;
537
538     if (( s = lp_conn_inet()) < 0 ) {
539         syslog( LOG_ERR, "lp_rmjob: %m" );
540         return( -1 );
541     }
542
543     if ( lp.lp_person == NULL ) {
544         return( -1 );
545     }
546
547     sprintf( buf, "\5%s %s %d\n", printer->p_printer, lp.lp_person, job );
548     n = strlen( buf );
549     if ( write( s, buf, n ) != n ) {
550         syslog( LOG_ERR, "lp_rmjob write: %m" );
551         lp_disconn_inet( s );
552         return( -1 );
553     }
554     while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
555         syslog( LOG_DEBUG, "read %.*s", n, buf );
556     }
557
558     lp_disconn_inet( s );
559     return( 0 );
560 }
561
562 char    *kw_rank = "Rank";
563 char    *kw_active = "active";
564
565 char    *tag_rank = "rank: ";
566 char    *tag_owner = "owner: ";
567 char    *tag_job = "job: ";
568 char    *tag_files = "files: ";
569 char    *tag_size = "size: ";
570 char    *tag_status = "status: ";
571
572 lp_queue( out )
573     struct papfile      *out;
574 {
575     char                        buf[ 1024 ], *start, *stop, *p, *q;
576     static struct papfile       pf;
577     int                         n, len, s;
578         
579     if (( s = lp_conn_unix()) < 0 ) {
580         syslog( LOG_ERR, "lp_queue: %m" );
581         return( -1 );
582     }
583
584     sprintf( buf, "\3%s\n", printer->p_printer );
585     n = strlen( buf );
586     if ( write( s, buf, n ) != n ) {
587         syslog( LOG_ERR, "lp_queue write: %m" );
588         lp_disconn_unix( s );
589         return( -1 );
590     }
591     pf.pf_state = PF_BOT;
592
593     while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
594         APPEND( &pf, buf, n );
595     }
596
597     for (;;) {
598         if ( markline( &start, &stop, &pf ) > 0 ) {
599             /* parse */
600             for ( p = start; p < stop; p++ ) {
601                 if ( *p == ' ' || *p == '\t' ) {
602                     break;
603                 }
604             }
605             if ( p >= stop ) {
606                 consumetomark( start, stop, &pf );
607                 continue;
608             }
609
610             /*
611              * Keys: "Rank", a number, "active"
612              * Anything else is status.
613              */
614             len = p - start;
615             if ( len == strlen( kw_rank ) &&
616                     strncmp( kw_rank, start, len ) == 0 ) {
617                 consumetomark( start, stop, &pf );
618                 continue;
619             }
620             if (( len == strlen( kw_active ) &&
621                     strncmp( kw_active, start, len ) == 0 ) ||
622                     isdigit( *start )) {                /* a job line */
623                 APPEND( out, tag_rank, strlen( tag_rank ));
624                 APPEND( out, start, p - start );
625                 APPEND( out, "\n", 1 );
626
627                 for ( ; p < stop; p++ ) {
628                     if ( *p != ' ' && *p != '\t' ) {
629                         break;
630                     }
631                 }
632                 for ( q = p; p < stop; p++ ) {
633                     if ( *p == ' ' || *p == '\t' ) {
634                         break;
635                     }
636                 }
637                 if ( p >= stop ) {
638                     APPEND( out, ".\n", 2 );
639                     consumetomark( start, stop, &pf );
640                     continue;
641                 }
642                 APPEND( out, tag_owner, strlen( tag_owner ));
643                 APPEND( out, q, p - q );
644                 APPEND( out, "\n", 1 );
645
646                 for ( ; p < stop; p++ ) {
647                     if ( *p != ' ' && *p != '\t' ) {
648                         break;
649                     }
650                 }
651                 for ( q = p; p < stop; p++ ) {
652                     if ( *p == ' ' || *p == '\t' ) {
653                         break;
654                     }
655                 }
656                 if ( p >= stop ) {
657                     APPEND( out, ".\n", 2 );
658                     consumetomark( start, stop, &pf );
659                     continue;
660                 }
661                 APPEND( out, tag_job, strlen( tag_job ));
662                 APPEND( out, q, p - q );
663                 APPEND( out, "\n", 1 );
664
665                 for ( ; p < stop; p++ ) {
666                     if ( *p != ' ' && *p != '\t' ) {
667                         break;
668                     }
669                 }
670                 for ( q = p, p = stop; p > q; p-- ) {
671                     if ( *p == ' ' || *p == '\t' ) {
672                         break;
673                     }
674                 }
675                 for ( ; p > q; p-- ) {
676                     if ( *p != ' ' && *p != '\t' ) {
677                         break;
678                     }
679                 }
680                 for ( ; p > q; p-- ) {
681                     if ( *p == ' ' || *p == '\t' ) {
682                         break;
683                     }
684                 }
685                 if ( p <= q ) {
686                     APPEND( out, ".\n", 2 );
687                     consumetomark( start, stop, &pf );
688                     continue;
689                 }
690                 APPEND( out, tag_files, strlen( tag_files ));
691                 APPEND( out, q, p - q );
692                 APPEND( out, "\n", 1 );
693
694                 for ( ; p < stop; p++ ) {
695                     if ( *p != ' ' && *p != '\t' ) {
696                         break;
697                     }
698                 }
699                 APPEND( out, tag_size, strlen( tag_size ));
700                 APPEND( out, p, stop - p );
701                 APPEND( out, "\n.\n", 3 );
702
703                 consumetomark( start, stop, &pf );
704                 continue;
705             }
706
707             /* status */
708             APPEND( out, tag_status, strlen( tag_status ));
709             APPEND( out, start, stop - start );
710             APPEND( out, "\n.\n", 3 );
711
712             consumetomark( start, stop, &pf );
713         } else {
714             APPEND( out, "*\n", 2 );
715             lp_disconn_unix( s );
716             return( 0 );
717         }
718     }
719 }