1 /*
2  * Copyright © 2008 Chris Wilson
3  *
4  * Permission to use, copy, modify, distribute, and sell this software
5  * and its documentation for any purpose is hereby granted without
6  * fee, provided that the above copyright notice appear in all copies
7  * and that both that copyright notice and this permission notice
8  * appear in supporting documentation, and that the name of
9  * Chris Wilson not be used in advertising or publicity pertaining to
10  * distribution of the software without specific, written prior
11  * permission. Chris Wilson makes no representations about the
12  * suitability of this software for any purpose.  It is provided "as
13  * is" without express or implied warranty.
14  *
15  * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
16  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17  * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
18  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
19  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
21  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author: Chris Wilson <chris@chris-wilson.co.uk>
24  *
25  * Contributor(s):
26  *	Carlos Garcia Campos <carlosgc@gnome.org>
27  *
28  * Adapted from pdf2png.c:
29  * Copyright © 2005 Red Hat, Inc.
30  *
31  * Permission to use, copy, modify, distribute, and sell this software
32  * and its documentation for any purpose is hereby granted without
33  * fee, provided that the above copyright notice appear in all copies
34  * and that both that copyright notice and this permission notice
35  * appear in supporting documentation, and that the name of
36  * Red Hat, Inc. not be used in advertising or publicity pertaining to
37  * distribution of the software without specific, written prior
38  * permission. Red Hat, Inc. makes no representations about the
39  * suitability of this software for any purpose.  It is provided "as
40  * is" without express or implied warranty.
41  *
42  * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
43  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
44  * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
45  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
46  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
47  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
48  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
49  *
50  * Author: Kristian Høgsberg <krh@redhat.com>
51  */
52 
53 #if HAVE_CONFIG_H
54 #include "config.h"
55 #endif
56 
57 #if HAVE_UNISTD_H
58 #include <unistd.h>
59 #endif
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 
64 #include <cairo.h>
65 #include <cairo-script-interpreter.h>
66 
67 #if CAIRO_CAN_TEST_PDF_SURFACE
68 #include <poppler.h>
69 #endif
70 
71 #if CAIRO_CAN_TEST_SVG_SURFACE
72 #include <librsvg/rsvg.h>
73 #ifndef RSVG_CAIRO_H
74 #include <librsvg/rsvg-cairo.h>
75 #endif
76 #endif
77 
78 #if CAIRO_HAS_SPECTRE
79 #include <libspectre/spectre.h>
80 #endif
81 
82 #include <errno.h>
83 
84 #if HAVE_FCNTL_H
85 #include <fcntl.h>
86 #endif
87 
88 #if HAVE_UNISTD_H && HAVE_SIGNAL_H && HAVE_SYS_STAT_H && HAVE_SYS_SOCKET_H && (HAVE_POLL_H || HAVE_SYS_POLL_H) && HAVE_SYS_UN_H
89 #include <signal.h>
90 #include <sys/stat.h>
91 #include <sys/socket.h>
92 #include <sys/un.h>
93 
94 #if defined(HAVE_POLL_H)
95 #include <poll.h>
96 #elif defined(HAVE_SYS_POLL_H)
97 #include <sys/poll.h>
98 #endif
99 
100 #define SOCKET_PATH "./.any2ppm"
101 #define TIMEOUT 60000 /* 60 seconds */
102 
103 #if HAVE_FORK
104 #define CAN_RUN_AS_DAEMON 1
105 #endif
106 #endif
107 
108 #define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
109 
110 static int
_cairo_writen(int fd,char * buf,int len)111 _cairo_writen (int fd, char *buf, int len)
112 {
113     while (len) {
114 	int ret;
115 
116 	ret = write (fd, buf, len);
117 	if (ret == -1) {
118 	    int err = errno;
119 	    switch (err) {
120 	    case EINTR:
121 	    case EAGAIN:
122 		continue;
123 	    default:
124 		return 0;
125 	    }
126 	}
127 	len -= ret;
128 	buf += ret;
129     }
130 
131     return 1;
132 }
133 
134 static int
_cairo_write(int fd,char * buf,int maxlen,int buflen,const unsigned char * src,int srclen)135 _cairo_write (int fd,
136 	char *buf, int maxlen, int buflen,
137 	const unsigned char *src, int srclen)
138 {
139     if (buflen < 0)
140 	return buflen;
141 
142     while (srclen) {
143 	int len;
144 
145 	len = buflen + srclen;
146 	if (len > maxlen)
147 	    len = maxlen;
148 	len -= buflen;
149 
150 	memcpy (buf + buflen, src, len);
151 	buflen += len;
152 	srclen -= len;
153 	src += len;
154 
155 	if (buflen == maxlen) {
156 	    if (! _cairo_writen (fd, buf, buflen))
157 		return -1;
158 
159 	    buflen = 0;
160 	}
161     }
162 
163     return buflen;
164 }
165 
166 static const char *
write_ppm(cairo_surface_t * surface,int fd)167 write_ppm (cairo_surface_t *surface, int fd)
168 {
169     char buf[4096];
170     cairo_format_t format;
171     const char *format_str;
172     const unsigned char *data;
173     int len;
174     int width, height, stride;
175     int i, j;
176 
177     data = cairo_image_surface_get_data (surface);
178     height = cairo_image_surface_get_height (surface);
179     width = cairo_image_surface_get_width (surface);
180     stride = cairo_image_surface_get_stride (surface);
181     format = cairo_image_surface_get_format (surface);
182     if (format == CAIRO_FORMAT_ARGB32) {
183 	/* see if we can convert to a standard ppm type and trim a few bytes */
184 	const unsigned char *alpha = data;
185 	for (j = height; j--; alpha += stride) {
186 	    for (i = 0; i < width; i++) {
187 		if ((*(unsigned int *) (alpha+4*i) & 0xff000000) != 0xff000000)
188 		    goto done;
189 	    }
190 	}
191 	format = CAIRO_FORMAT_RGB24;
192  done: ;
193     }
194 
195     switch (format) {
196     case CAIRO_FORMAT_ARGB32:
197 	/* XXX need true alpha for svg */
198 	format_str = "P7";
199 	break;
200     case CAIRO_FORMAT_RGB24:
201 	format_str = "P6";
202 	break;
203     case CAIRO_FORMAT_A8:
204 	format_str = "P5";
205 	break;
206     case CAIRO_FORMAT_A1:
207     case CAIRO_FORMAT_RGB16_565:
208     case CAIRO_FORMAT_RGB30:
209     case CAIRO_FORMAT_RGB96F:
210     case CAIRO_FORMAT_RGBA128F:
211     case CAIRO_FORMAT_INVALID:
212     default:
213 	return "unhandled image format";
214     }
215 
216     len = sprintf (buf, "%s %d %d 255\n", format_str, width, height);
217     for (j = 0; j < height; j++) {
218 	const unsigned int *row = (unsigned int *) (data + stride * j);
219 
220 	switch ((int) format) {
221 	case CAIRO_FORMAT_ARGB32:
222 	    len = _cairo_write (fd,
223 			  buf, sizeof (buf), len,
224 			  (unsigned char *) row, 4 * width);
225 	    break;
226 	case CAIRO_FORMAT_RGB24:
227 	    for (i = 0; i < width; i++) {
228 		unsigned char rgb[3];
229 		unsigned int p = *row++;
230 		rgb[0] = (p & 0xff0000) >> 16;
231 		rgb[1] = (p & 0x00ff00) >> 8;
232 		rgb[2] = (p & 0x0000ff) >> 0;
233 		len = _cairo_write (fd,
234 			      buf, sizeof (buf), len,
235 			      rgb, 3);
236 	    }
237 	    break;
238 	case CAIRO_FORMAT_A8:
239 	    len = _cairo_write (fd,
240 			  buf, sizeof (buf), len,
241 			  (unsigned char *) row, width);
242 	    break;
243 	}
244 	if (len < 0)
245 	    return "write failed";
246     }
247 
248     if (len && ! _cairo_writen (fd, buf, len))
249 	return "write failed";
250 
251     return NULL;
252 }
253 
254 #if CAIRO_HAS_INTERPRETER
255 static cairo_surface_t *
_create_image(void * closure,cairo_content_t content,double width,double height,long uid)256 _create_image (void *closure,
257 	       cairo_content_t content,
258 	       double width, double height,
259 	       long uid)
260 {
261     cairo_surface_t **out = closure;
262     cairo_format_t format;
263     switch (content) {
264     case CAIRO_CONTENT_ALPHA:
265 	format = CAIRO_FORMAT_A8;
266 	break;
267     case CAIRO_CONTENT_COLOR:
268 	format = CAIRO_FORMAT_RGB24;
269 	break;
270     default:
271     case CAIRO_CONTENT_COLOR_ALPHA:
272 	format = CAIRO_FORMAT_ARGB32;
273 	break;
274     }
275     *out = cairo_image_surface_create (format, width, height);
276     return cairo_surface_reference (*out);
277 }
278 
279 static const char *
_cairo_script_render_page(const char * filename,cairo_surface_t ** surface_out)280 _cairo_script_render_page (const char *filename,
281 			   cairo_surface_t **surface_out)
282 {
283     cairo_script_interpreter_t *csi;
284     cairo_surface_t *surface = NULL;
285     cairo_status_t status;
286     const cairo_script_interpreter_hooks_t hooks = {
287 	&surface,
288 	_create_image,
289 	NULL, /* surface_destroy */
290 	NULL, /* context_create */
291 	NULL, /* context_destroy */
292 	NULL, /* show_page */
293 	NULL  /* copy_page */
294     };
295 
296     csi = cairo_script_interpreter_create ();
297     cairo_script_interpreter_install_hooks (csi, &hooks);
298     status = cairo_script_interpreter_run (csi, filename);
299     if (status) {
300 	cairo_surface_destroy (surface);
301 	surface = NULL;
302     }
303     status = cairo_script_interpreter_destroy (csi);
304     if (surface == NULL)
305 	return "cairo-script interpreter failed";
306 
307     if (status == CAIRO_STATUS_SUCCESS)
308 	status = cairo_surface_status (surface);
309     if (status) {
310 	cairo_surface_destroy (surface);
311 	return cairo_status_to_string (status);
312     }
313 
314     *surface_out = surface;
315     return NULL;
316 }
317 
318 static const char *
cs_convert(char ** argv,int fd)319 cs_convert (char **argv, int fd)
320 {
321     const char *err;
322     cairo_surface_t *surface = NULL; /* silence compiler warning */
323 
324     err = _cairo_script_render_page (argv[0], &surface);
325     if (err != NULL)
326 	return err;
327 
328     err = write_ppm (surface, fd);
329     cairo_surface_destroy (surface);
330 
331     return err;
332 }
333 #else
334 static const char *
cs_convert(char ** argv,int fd)335 cs_convert (char **argv, int fd)
336 {
337     return "compiled without CairoScript support.";
338 }
339 #endif
340 
341 #if CAIRO_CAN_TEST_PDF_SURFACE
342 /* adapted from pdf2png.c */
343 static const char *
_poppler_render_page(const char * filename,const char * page_label,cairo_surface_t ** surface_out)344 _poppler_render_page (const char *filename,
345 		      const char *page_label,
346 		      cairo_surface_t **surface_out)
347 {
348     PopplerDocument *document;
349     PopplerPage *page;
350     double width, height;
351     GError *error = NULL;
352     gchar *absolute, *uri;
353     cairo_surface_t *surface;
354     cairo_t *cr;
355     cairo_status_t status;
356 
357     if (g_path_is_absolute (filename)) {
358 	absolute = g_strdup (filename);
359     } else {
360 	gchar *dir = g_get_current_dir ();
361 	absolute = g_build_filename (dir, filename, (gchar *) 0);
362 	g_free (dir);
363     }
364 
365     uri = g_filename_to_uri (absolute, NULL, &error);
366     g_free (absolute);
367     if (uri == NULL)
368 	return error->message; /* XXX g_error_free (error) */
369 
370     document = poppler_document_new_from_file (uri, NULL, &error);
371     g_free (uri);
372     if (document == NULL)
373 	return error->message; /* XXX g_error_free (error) */
374 
375     page = poppler_document_get_page_by_label (document, page_label);
376     g_object_unref (document);
377     if (page == NULL)
378 	return "page not found";
379 
380     poppler_page_get_size (page, &width, &height);
381 
382     surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
383     cr = cairo_create (surface);
384 
385     cairo_set_source_rgb (cr, 1., 1., 1.);
386     cairo_paint (cr);
387     cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
388 
389     poppler_page_render (page, cr);
390     g_object_unref (page);
391 
392     cairo_pop_group_to_source (cr);
393     cairo_paint (cr);
394 
395     status = cairo_status (cr);
396     cairo_destroy (cr);
397 
398     if (status) {
399 	cairo_surface_destroy (surface);
400 	return  cairo_status_to_string (status);
401     }
402 
403     *surface_out = surface;
404     return NULL;
405 }
406 
407 static const char *
pdf_convert(char ** argv,int fd)408 pdf_convert (char **argv, int fd)
409 {
410     const char *err;
411     cairo_surface_t *surface = NULL; /* silence compiler warning */
412 
413     err = _poppler_render_page (argv[0], argv[1], &surface);
414     if (err != NULL)
415 	return err;
416 
417     err = write_ppm (surface, fd);
418     cairo_surface_destroy (surface);
419 
420     return err;
421 }
422 #else
423 static const char *
pdf_convert(char ** argv,int fd)424 pdf_convert (char **argv, int fd)
425 {
426     return "compiled without PDF support.";
427 }
428 #endif
429 
430 #if CAIRO_CAN_TEST_SVG_SURFACE
431 static const char *
_rsvg_render_page(const char * filename,cairo_surface_t ** surface_out)432 _rsvg_render_page (const char *filename,
433 		   cairo_surface_t **surface_out)
434 {
435     RsvgHandle *handle;
436     RsvgDimensionData dimensions;
437     GError *error = NULL;
438     cairo_surface_t *surface;
439     cairo_t *cr;
440     cairo_status_t status;
441 
442     handle = rsvg_handle_new_from_file (filename, &error);
443     if (handle == NULL)
444 	return error->message; /* XXX g_error_free */
445 
446     rsvg_handle_set_dpi (handle, 72.0);
447     rsvg_handle_get_dimensions (handle, &dimensions);
448     surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
449 					  dimensions.width,
450 					  dimensions.height);
451     cr = cairo_create (surface);
452 
453     rsvg_handle_render_cairo (handle, cr);
454     g_object_unref (handle);
455 
456     status = cairo_status (cr);
457     cairo_destroy (cr);
458 
459     if (status) {
460 	cairo_surface_destroy (surface);
461 	return  cairo_status_to_string (status);
462     }
463 
464     *surface_out = surface;
465     return NULL;
466 }
467 
468 static const char *
svg_convert(char ** argv,int fd)469 svg_convert (char **argv, int fd)
470 {
471     const char *err;
472     cairo_surface_t *surface = NULL; /* silence compiler warning */
473 
474     err = _rsvg_render_page (argv[0], &surface);
475     if (err != NULL)
476 	return err;
477 
478     err = write_ppm (surface, fd);
479     cairo_surface_destroy (surface);
480 
481     return err;
482 }
483 #else
484 static const char *
svg_convert(char ** argv,int fd)485 svg_convert (char **argv, int fd)
486 {
487     return "compiled without SVG support.";
488 }
489 #endif
490 
491 #if CAIRO_HAS_SPECTRE
492 static const char *
_spectre_render_page(const char * filename,const char * page_label,cairo_surface_t ** surface_out)493 _spectre_render_page (const char *filename,
494 		      const char *page_label,
495 		      cairo_surface_t **surface_out)
496 {
497     static const cairo_user_data_key_t key;
498 
499     SpectreDocument *document;
500     SpectreStatus status;
501     int width, height, stride;
502     unsigned char *pixels;
503     cairo_surface_t *surface;
504 
505     document = spectre_document_new ();
506     spectre_document_load (document, filename);
507     status = spectre_document_status (document);
508     if (status) {
509 	spectre_document_free (document);
510 	return spectre_status_to_string (status);
511     }
512 
513     if (page_label) {
514 	SpectrePage *page;
515 	SpectreRenderContext *rc;
516 
517 	page = spectre_document_get_page_by_label (document, page_label);
518 	spectre_document_free (document);
519 	if (page == NULL)
520 	    return "page not found";
521 
522 	spectre_page_get_size (page, &width, &height);
523 	rc = spectre_render_context_new ();
524 	spectre_render_context_set_page_size (rc, width, height);
525 	spectre_page_render (page, rc, &pixels, &stride);
526 	spectre_render_context_free (rc);
527 	status = spectre_page_status (page);
528 	spectre_page_free (page);
529 	if (status) {
530 	    free (pixels);
531 	    return spectre_status_to_string (status);
532 	}
533     } else {
534 	spectre_document_get_page_size (document, &width, &height);
535 	spectre_document_render (document, &pixels, &stride);
536 	spectre_document_free (document);
537     }
538 
539     surface = cairo_image_surface_create_for_data (pixels,
540 						   CAIRO_FORMAT_RGB24,
541 						   width, height,
542 						   stride);
543     cairo_surface_set_user_data (surface, &key,
544 				 pixels, (cairo_destroy_func_t) free);
545     *surface_out = surface;
546     return NULL;
547 }
548 
549 static const char *
ps_convert(char ** argv,int fd)550 ps_convert (char **argv, int fd)
551 {
552     const char *err;
553     cairo_surface_t *surface = NULL; /* silence compiler warning */
554 
555     err = _spectre_render_page (argv[0], argv[1], &surface);
556     if (err != NULL)
557 	return err;
558 
559     err = write_ppm (surface, fd);
560     cairo_surface_destroy (surface);
561 
562     return err;
563 }
564 #else
565 static const char *
ps_convert(char ** argv,int fd)566 ps_convert (char **argv, int fd)
567 {
568     return "compiled without PostScript support.";
569 }
570 #endif
571 
572 static const char *
convert(char ** argv,int fd)573 convert (char **argv, int fd)
574 {
575     static const struct converter {
576 	const char *type;
577 	const char *(*func) (char **, int);
578     } converters[] = {
579 	{ "cs", cs_convert },
580 	{ "pdf", pdf_convert },
581 	{ "ps", ps_convert },
582 	{ "svg", svg_convert },
583 	{ NULL, NULL }
584     };
585     const struct converter *converter = converters;
586     char *type;
587 
588     type = strrchr (argv[0], '.');
589     if (type == NULL)
590 	return "no file extension";
591     type++;
592 
593     while (converter->type) {
594 	if (strcmp (type, converter->type) == 0)
595 	    return converter->func (argv, fd);
596 	converter++;
597     }
598     return "no converter";
599 }
600 
601 #if CAN_RUN_AS_DAEMON
602 static int
_getline(int fd,char ** linep,size_t * lenp)603 _getline (int fd, char **linep, size_t *lenp)
604 {
605     char *line;
606     size_t len, i;
607     ssize_t ret;
608 
609     line = *linep;
610     if (line == NULL) {
611 	line = malloc (1024);
612 	if (line == NULL)
613 	    return -1;
614 	line[0] = '\0';
615 	len = 1024;
616     } else
617 	len = *lenp;
618 
619     /* XXX simple, but ugly! */
620     i = 0;
621     do {
622 	if (i == len - 1) {
623 	    char *nline;
624 
625 	    nline = realloc (line, len + 1024);
626 	    if (nline == NULL)
627 		goto out;
628 
629 	    line = nline;
630 	    len += 1024;
631 	}
632 
633 	ret = read (fd, line + i, 1);
634 	if (ret == -1 || ret == 0)
635 	    goto out;
636     } while (line[i++] != '\n');
637 
638 out:
639     line[i] = '\0';
640     *linep = line;
641     *lenp = len;
642     return i-1;
643 }
644 
645 static int
split_line(char * line,char * argv[],int max_argc)646 split_line (char *line, char *argv[], int max_argc)
647 {
648     int i = 0;
649 
650     max_argc--; /* leave one spare for the trailing NULL */
651 
652     argv[i++] = line;
653     while (i < max_argc && (line = strchr (line, ' ')) != NULL) {
654 	*line++ = '\0';
655 	argv[i++] = line;
656     }
657 
658     /* chomp the newline */
659     line = strchr (argv[i-1], '\n');
660     if (line != NULL)
661 	*line = '\0';
662 
663     argv[i] = NULL;
664 
665     return i;
666 }
667 
668 static int
any2ppm_daemon_exists(void)669 any2ppm_daemon_exists (void)
670 {
671     struct stat st;
672     int fd;
673     char buf[80];
674     int pid;
675     int ret;
676 
677     if (stat (SOCKET_PATH, &st) < 0)
678 	return 0;
679 
680     fd = open (SOCKET_PATH ".pid", O_RDONLY);
681     if (fd < 0)
682 	return 0;
683 
684     pid = 0;
685     ret = read (fd, buf, sizeof (buf) - 1);
686     if (ret > 0) {
687 	buf[ret] = '\0';
688 	pid = atoi (buf);
689     }
690     close (fd);
691 
692     return pid > 0 && kill (pid, 0) == 0;
693 }
694 
695 static int
write_pid_file(void)696 write_pid_file (void)
697 {
698     int fd;
699     char buf[80];
700     int ret;
701 
702     fd = open (SOCKET_PATH ".pid", O_CREAT | O_TRUNC | O_WRONLY, 0666);
703     if (fd < 0)
704 	return 0;
705 
706     ret = sprintf (buf, "%d\n", getpid ());
707     ret = write (fd, buf, ret) == ret;
708     close (fd);
709 
710     return ret;
711 }
712 
713 static int
open_devnull_to_fd(int want_fd,int flags)714 open_devnull_to_fd (int want_fd, int flags)
715 {
716     int error;
717     int got_fd;
718 
719     close (want_fd);
720 
721     got_fd = open("/dev/null", flags | O_CREAT, 0700);
722     if (got_fd == -1)
723         return -1;
724 
725     error = dup2 (got_fd, want_fd);
726     close (got_fd);
727 
728     return error;
729 }
730 
731 static int
daemonize(void)732 daemonize (void)
733 {
734     void (*oldhup) (int);
735 
736     /* Let the parent go. */
737     switch (fork ()) {
738     case -1: return -1;
739     case 0: break;
740     default: _exit (0);
741     }
742 
743     /* Become session leader. */
744     if (setsid () == -1)
745 	return -1;
746 
747     /* Refork to yield session leadership. */
748     oldhup = signal (SIGHUP, SIG_IGN);
749 
750     switch (fork ()) {		/* refork to yield session leadership. */
751     case -1: return -1;
752     case 0: break;
753     default: _exit (0);
754     }
755 
756     signal (SIGHUP, oldhup);
757 
758     /* Establish stdio. */
759     if (open_devnull_to_fd (0, O_RDONLY) == -1)
760 	return -1;
761     if (open_devnull_to_fd (1, O_WRONLY | O_APPEND) == -1)
762 	return -1;
763     if (dup2 (1, 2) == -1)
764 	return -1;
765 
766     return 0;
767 }
768 
769 static const char *
any2ppm_daemon(void)770 any2ppm_daemon (void)
771 {
772     int timeout = TIMEOUT;
773     struct pollfd pfd;
774     int sk, fd;
775     long flags;
776     struct sockaddr_un addr;
777     char *line = NULL;
778     size_t len = 0;
779 
780 #ifdef SIGPIPE
781     signal (SIGPIPE, SIG_IGN);
782 #endif
783 
784     /* XXX racy! */
785     if (getenv ("ANY2PPM_FORCE") == NULL && any2ppm_daemon_exists ())
786 	return "any2ppm daemon already running";
787 
788     unlink (SOCKET_PATH);
789 
790     sk = socket (PF_UNIX, SOCK_STREAM, 0);
791     if (sk == -1)
792 	return "unable to create socket";
793 
794     memset (&addr, 0, sizeof (addr));
795     addr.sun_family = AF_UNIX;
796     strcpy (addr.sun_path, SOCKET_PATH);
797     if (bind (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
798 	close (sk);
799 	return "unable to bind socket";
800     }
801 
802     flags = fcntl (sk, F_GETFL);
803     if (flags == -1 || fcntl (sk, F_SETFL, flags | O_NONBLOCK) == -1) {
804 	close (sk);
805 	return "unable to set socket to non-blocking";
806     }
807 
808     if (listen (sk, 5) == -1) {
809 	close (sk);
810 	return "unable to listen on socket";
811     }
812 
813     /* ready for client connection - detach from parent/terminal */
814     if (getenv ("ANY2PPM_NODAEMON") == NULL && daemonize () == -1) {
815 	close (sk);
816 	return "unable to detach from parent";
817     }
818 
819     if (! write_pid_file ()) {
820 	close (sk);
821 	return "unable to write pid file";
822     }
823 
824     if (getenv ("ANY2PPM_TIMEOUT") != NULL) {
825 	timeout = atoi (getenv ("ANY2PPM_TIMEOUT"));
826 	if (timeout == 0)
827 	    timeout = -1;
828 	if (timeout > 0)
829 	    timeout *= 1000; /* convert env (in seconds) to milliseconds */
830     }
831 
832     pfd.fd = sk;
833     pfd.events = POLLIN;
834     pfd.revents = 0; /* valgrind */
835     while (poll (&pfd, 1, timeout) > 0) {
836 	while ((fd = accept (sk, NULL, NULL)) != -1) {
837 	    if (_getline (fd, &line, &len) != -1) {
838 		char *argv[10];
839 
840 		if (split_line (line, argv, ARRAY_LENGTH (argv)) > 0) {
841 		    const char *err;
842 
843 		    err = convert (argv, fd);
844 		    if (err != NULL) {
845 			FILE *file = fopen (".any2ppm.errors", "a");
846 			if (file != NULL) {
847 			    fprintf (file,
848 				     "Failed to convert '%s': %s\n",
849 				     argv[0], err);
850 			    fclose (file);
851 			}
852 		    }
853 		}
854 	    }
855 	    close (fd);
856 	}
857     }
858     close (sk);
859     unlink (SOCKET_PATH);
860     unlink (SOCKET_PATH ".pid");
861 
862     free (line);
863     return NULL;
864 }
865 #else
866 static const char *
any2ppm_daemon(void)867 any2ppm_daemon (void)
868 {
869     return "daemon not compiled in.";
870 }
871 #endif
872 
873 int
main(int argc,char ** argv)874 main (int argc, char **argv)
875 {
876     const char *err;
877 
878 #if CAIRO_CAN_TEST_PDF_SURFACE || CAIRO_CAN_TEST_SVG_SURFACE
879 #if GLIB_MAJOR_VERSION <= 2 && GLIB_MINOR_VERSION <= 34
880     g_type_init ();
881 #endif
882 #endif
883 
884 #if defined(_WIN32) && !defined (__CYGWIN__)
885     _setmode (1, _O_BINARY);
886 #endif
887 
888     if (argc == 1)
889 	err = any2ppm_daemon ();
890     else
891 	err = convert (argv + 1, 1);
892     if (err != NULL) {
893 	fprintf (stderr, "Failed to run converter: %s\n", err);
894 	return EXIT_FAILURE;
895     }
896 
897     return EXIT_SUCCESS;
898 }
899