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
#include "config.h"
54

            
55
#if HAVE_UNISTD_H
56
#include <unistd.h>
57
#endif
58
#include <stdio.h>
59
#include <stdlib.h>
60
#include <string.h>
61

            
62
#include <cairo.h>
63

            
64
#if CAIRO_HAS_INTERPRETER
65
#include <cairo-script-interpreter.h>
66
#endif
67

            
68
#if CAIRO_CAN_TEST_PDF_SURFACE
69
#include <poppler.h>
70
#endif
71

            
72
#if CAIRO_CAN_TEST_SVG_SURFACE
73
#define RSVG_DISABLE_DEPRECATION_WARNINGS
74
#include <librsvg/rsvg.h>
75
#ifndef RSVG_CAIRO_H
76
#include <librsvg/rsvg-cairo.h>
77
#endif
78
#endif
79

            
80
#if CAIRO_HAS_SPECTRE
81
#include <libspectre/spectre.h>
82
#endif
83

            
84
#include <errno.h>
85

            
86
#if HAVE_FCNTL_H
87
#include <fcntl.h>
88
#endif
89

            
90
#ifdef _WIN32
91
#include <io.h>
92
#endif
93

            
94
#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
95
#include <signal.h>
96
#include <sys/stat.h>
97
#include <sys/socket.h>
98
#include <sys/un.h>
99

            
100
#if defined(HAVE_POLL_H)
101
#include <poll.h>
102
#elif defined(HAVE_SYS_POLL_H)
103
#include <sys/poll.h>
104
#endif
105

            
106
#define SOCKET_PATH "./.any2ppm"
107
#define TIMEOUT 60000 /* 60 seconds */
108

            
109
#if HAVE_FORK
110
#define CAN_RUN_AS_DAEMON 1
111
#endif
112
#endif
113

            
114
#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
115

            
116
static int
117
_cairo_writen (int fd, char *buf, int len)
118
{
119
    while (len) {
120
	int ret;
121

            
122
	ret = write (fd, buf, len);
123
	if (ret == -1) {
124
	    int err = errno;
125
	    switch (err) {
126
	    case EINTR:
127
	    case EAGAIN:
128
		continue;
129
	    default:
130
		return 0;
131
	    }
132
	}
133
	len -= ret;
134
	buf += ret;
135
    }
136

            
137
    return 1;
138
}
139

            
140
static int
141
_cairo_write (int fd,
142
	char *buf, int maxlen, int buflen,
143
	const unsigned char *src, int srclen)
144
{
145
    if (buflen < 0)
146
	return buflen;
147

            
148
    while (srclen) {
149
	int len;
150

            
151
	len = buflen + srclen;
152
	if (len > maxlen)
153
	    len = maxlen;
154
	len -= buflen;
155

            
156
	memcpy (buf + buflen, src, len);
157
	buflen += len;
158
	srclen -= len;
159
	src += len;
160

            
161
	if (buflen == maxlen) {
162
	    if (! _cairo_writen (fd, buf, buflen))
163
		return -1;
164

            
165
	    buflen = 0;
166
	}
167
    }
168

            
169
    return buflen;
170
}
171

            
172
static const char *
173
write_ppm (cairo_surface_t *surface, int fd)
174
{
175
    char buf[4096];
176
    cairo_format_t format;
177
    const char *format_str;
178
    const unsigned char *data;
179
    int len;
180
    int width, height, stride;
181
    int i, j;
182

            
183
    data = cairo_image_surface_get_data (surface);
184
    height = cairo_image_surface_get_height (surface);
185
    width = cairo_image_surface_get_width (surface);
186
    stride = cairo_image_surface_get_stride (surface);
187
    format = cairo_image_surface_get_format (surface);
188
    if (format == CAIRO_FORMAT_ARGB32) {
189
	/* see if we can convert to a standard ppm type and trim a few bytes */
190
	const unsigned char *alpha = data;
191
	for (j = height; j--; alpha += stride) {
192
	    for (i = 0; i < width; i++) {
193
		if ((*(unsigned int *) (alpha+4*i) & 0xff000000) != 0xff000000)
194
		    goto done;
195
	    }
196
	}
197
	format = CAIRO_FORMAT_RGB24;
198
 done: ;
199
    }
200

            
201
    switch (format) {
202
    case CAIRO_FORMAT_ARGB32:
203
	/* XXX need true alpha for svg */
204
	format_str = "P7";
205
	break;
206
    case CAIRO_FORMAT_RGB24:
207
	format_str = "P6";
208
	break;
209
    case CAIRO_FORMAT_A8:
210
	format_str = "P5";
211
	break;
212
    case CAIRO_FORMAT_A1:
213
    case CAIRO_FORMAT_RGB16_565:
214
    case CAIRO_FORMAT_RGB30:
215
    case CAIRO_FORMAT_RGB96F:
216
    case CAIRO_FORMAT_RGBA128F:
217
    case CAIRO_FORMAT_INVALID:
218
    default:
219
	return "unhandled image format";
220
    }
221

            
222
    len = sprintf (buf, "%s %d %d 255\n", format_str, width, height);
223
    for (j = 0; j < height; j++) {
224
	const unsigned int *row = (unsigned int *) (data + stride * j);
225

            
226
	switch ((int) format) {
227
	case CAIRO_FORMAT_ARGB32:
228
	    len = _cairo_write (fd,
229
			  buf, sizeof (buf), len,
230
			  (unsigned char *) row, 4 * width);
231
	    break;
232
	case CAIRO_FORMAT_RGB24:
233
	    for (i = 0; i < width; i++) {
234
		unsigned char rgb[3];
235
		unsigned int p = *row++;
236
		rgb[0] = (p & 0xff0000) >> 16;
237
		rgb[1] = (p & 0x00ff00) >> 8;
238
		rgb[2] = (p & 0x0000ff) >> 0;
239
		len = _cairo_write (fd,
240
			      buf, sizeof (buf), len,
241
			      rgb, 3);
242
	    }
243
	    break;
244
	case CAIRO_FORMAT_A8:
245
	    len = _cairo_write (fd,
246
			  buf, sizeof (buf), len,
247
			  (unsigned char *) row, width);
248
	    break;
249
	}
250
	if (len < 0)
251
	    return "write failed";
252
    }
253

            
254
    if (len && ! _cairo_writen (fd, buf, len))
255
	return "write failed";
256

            
257
    return NULL;
258
}
259

            
260
#if CAIRO_HAS_INTERPRETER
261
static cairo_surface_t *
262
_create_image (void *closure,
263
	       cairo_content_t content,
264
	       double width, double height,
265
	       long uid)
266
{
267
    cairo_surface_t **out = closure;
268
    cairo_format_t format;
269
    switch (content) {
270
    case CAIRO_CONTENT_ALPHA:
271
	format = CAIRO_FORMAT_A8;
272
	break;
273
    case CAIRO_CONTENT_COLOR:
274
	format = CAIRO_FORMAT_RGB24;
275
	break;
276
    default:
277
    case CAIRO_CONTENT_COLOR_ALPHA:
278
	format = CAIRO_FORMAT_ARGB32;
279
	break;
280
    }
281
    *out = cairo_image_surface_create (format, width, height);
282
    return cairo_surface_reference (*out);
283
}
284

            
285
static const char *
286
_cairo_script_render_page (const char *filename,
287
			   cairo_surface_t **surface_out)
288
{
289
    cairo_script_interpreter_t *csi;
290
    cairo_surface_t *surface = NULL;
291
    cairo_status_t status;
292
    const cairo_script_interpreter_hooks_t hooks = {
293
	&surface,
294
	_create_image,
295
	NULL, /* surface_destroy */
296
	NULL, /* context_create */
297
	NULL, /* context_destroy */
298
	NULL, /* show_page */
299
	NULL  /* copy_page */
300
    };
301

            
302
    csi = cairo_script_interpreter_create ();
303
    cairo_script_interpreter_install_hooks (csi, &hooks);
304
    status = cairo_script_interpreter_run (csi, filename);
305
    if (status) {
306
	cairo_surface_destroy (surface);
307
	surface = NULL;
308
    }
309
    status = cairo_script_interpreter_destroy (csi);
310
    if (surface == NULL)
311
	return "cairo-script interpreter failed";
312

            
313
    if (status == CAIRO_STATUS_SUCCESS)
314
	status = cairo_surface_status (surface);
315
    if (status) {
316
	cairo_surface_destroy (surface);
317
	return cairo_status_to_string (status);
318
    }
319

            
320
    *surface_out = surface;
321
    return NULL;
322
}
323

            
324
static const char *
325
cs_convert (char **argv, int fd)
326
{
327
    const char *err;
328
    cairo_surface_t *surface = NULL; /* silence compiler warning */
329

            
330
    err = _cairo_script_render_page (argv[0], &surface);
331
    if (err != NULL)
332
	return err;
333

            
334
    err = write_ppm (surface, fd);
335
    cairo_surface_destroy (surface);
336

            
337
    return err;
338
}
339
#else
340
static const char *
341
cs_convert (char **argv, int fd)
342
{
343
    return "compiled without CairoScript support.";
344
}
345
#endif
346

            
347
#if CAIRO_CAN_TEST_PDF_SURFACE
348
/* adapted from pdf2png.c */
349
static const char *
350
_poppler_render_page (const char *filename,
351
		      const char *page_label,
352
		      cairo_surface_t **surface_out)
353
{
354
    PopplerDocument *document;
355
    PopplerPage *page;
356
    double width, height;
357
    GError *error = NULL;
358
    gchar *absolute, *uri;
359
    cairo_surface_t *surface;
360
    cairo_t *cr;
361
    cairo_status_t status;
362

            
363
    if (g_path_is_absolute (filename)) {
364
	absolute = g_strdup (filename);
365
    } else {
366
	gchar *dir = g_get_current_dir ();
367
	absolute = g_build_filename (dir, filename, (gchar *) 0);
368
	g_free (dir);
369
    }
370

            
371
    uri = g_filename_to_uri (absolute, NULL, &error);
372
    g_free (absolute);
373
    if (uri == NULL)
374
	return error->message; /* XXX g_error_free (error) */
375

            
376
    document = poppler_document_new_from_file (uri, NULL, &error);
377
    g_free (uri);
378
    if (document == NULL)
379
	return error->message; /* XXX g_error_free (error) */
380

            
381
    page = poppler_document_get_page_by_label (document, page_label);
382
    g_object_unref (document);
383
    if (page == NULL)
384
	return "page not found";
385

            
386
    poppler_page_get_size (page, &width, &height);
387

            
388
    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
389
    cr = cairo_create (surface);
390

            
391
    cairo_set_source_rgb (cr, 1., 1., 1.);
392
    cairo_paint (cr);
393
    cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
394

            
395
    poppler_page_render (page, cr);
396
    g_object_unref (page);
397

            
398
    cairo_pop_group_to_source (cr);
399
    cairo_paint (cr);
400

            
401
    status = cairo_status (cr);
402
    cairo_destroy (cr);
403

            
404
    if (status) {
405
	cairo_surface_destroy (surface);
406
	return  cairo_status_to_string (status);
407
    }
408

            
409
    *surface_out = surface;
410
    return NULL;
411
}
412

            
413
static const char *
414
pdf_convert (char **argv, int fd)
415
{
416
    const char *err;
417
    cairo_surface_t *surface = NULL; /* silence compiler warning */
418

            
419
    err = _poppler_render_page (argv[0], argv[1], &surface);
420
    if (err != NULL)
421
	return err;
422

            
423
    err = write_ppm (surface, fd);
424
    cairo_surface_destroy (surface);
425

            
426
    return err;
427
}
428
#else
429
static const char *
430
pdf_convert (char **argv, int fd)
431
{
432
    return "compiled without PDF support.";
433
}
434
#endif
435

            
436
#if CAIRO_CAN_TEST_SVG_SURFACE
437
static const char *
438
_rsvg_render_page (const char *filename,
439
		   cairo_surface_t **surface_out)
440
{
441
    RsvgHandle *handle;
442
    RsvgDimensionData dimensions;
443
    GError *error = NULL;
444
    cairo_surface_t *surface;
445
    cairo_t *cr;
446
    cairo_status_t status;
447

            
448
    handle = rsvg_handle_new_from_file (filename, &error);
449
    if (handle == NULL)
450
	return error->message; /* XXX g_error_free */
451

            
452
    rsvg_handle_set_dpi (handle, 72.0);
453
    rsvg_handle_get_dimensions (handle, &dimensions);
454
    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
455
					  dimensions.width,
456
					  dimensions.height);
457
    cr = cairo_create (surface);
458

            
459
    rsvg_handle_render_cairo (handle, cr);
460
    g_object_unref (handle);
461

            
462
    status = cairo_status (cr);
463
    cairo_destroy (cr);
464

            
465
    if (status) {
466
	cairo_surface_destroy (surface);
467
	return  cairo_status_to_string (status);
468
    }
469

            
470
    *surface_out = surface;
471
    return NULL;
472
}
473

            
474
static const char *
475
svg_convert (char **argv, int fd)
476
{
477
    const char *err;
478
    cairo_surface_t *surface = NULL; /* silence compiler warning */
479

            
480
    err = _rsvg_render_page (argv[0], &surface);
481
    if (err != NULL)
482
	return err;
483

            
484
    err = write_ppm (surface, fd);
485
    cairo_surface_destroy (surface);
486

            
487
    return err;
488
}
489
#else
490
static const char *
491
svg_convert (char **argv, int fd)
492
{
493
    return "compiled without SVG support.";
494
}
495
#endif
496

            
497
#if CAIRO_HAS_SPECTRE
498
static const char *
499
_spectre_render_page (const char *filename,
500
		      const char *page_label,
501
		      cairo_surface_t **surface_out)
502
{
503
    static const cairo_user_data_key_t key;
504

            
505
    SpectreDocument *document;
506
    SpectreStatus status;
507
    int width, height, stride;
508
    unsigned char *pixels;
509
    cairo_surface_t *surface;
510

            
511
    document = spectre_document_new ();
512
    spectre_document_load (document, filename);
513
    status = spectre_document_status (document);
514
    if (status) {
515
	spectre_document_free (document);
516
	return spectre_status_to_string (status);
517
    }
518

            
519
    if (page_label) {
520
	SpectrePage *page;
521
	SpectreRenderContext *rc;
522

            
523
	page = spectre_document_get_page_by_label (document, page_label);
524
	spectre_document_free (document);
525
	if (page == NULL)
526
	    return "page not found";
527

            
528
	spectre_page_get_size (page, &width, &height);
529
	rc = spectre_render_context_new ();
530
	spectre_render_context_set_page_size (rc, width, height);
531
	spectre_page_render (page, rc, &pixels, &stride);
532
	spectre_render_context_free (rc);
533
	status = spectre_page_status (page);
534
	spectre_page_free (page);
535
	if (status) {
536
	    free (pixels);
537
	    return spectre_status_to_string (status);
538
	}
539
    } else {
540
	spectre_document_get_page_size (document, &width, &height);
541
	spectre_document_render (document, &pixels, &stride);
542
	spectre_document_free (document);
543
    }
544

            
545
    surface = cairo_image_surface_create_for_data (pixels,
546
						   CAIRO_FORMAT_RGB24,
547
						   width, height,
548
						   stride);
549
    cairo_surface_set_user_data (surface, &key,
550
				 pixels, (cairo_destroy_func_t) free);
551
    *surface_out = surface;
552
    return NULL;
553
}
554

            
555
static const char *
556
ps_convert (char **argv, int fd)
557
{
558
    const char *err;
559
    cairo_surface_t *surface = NULL; /* silence compiler warning */
560

            
561
    err = _spectre_render_page (argv[0], argv[1], &surface);
562
    if (err != NULL)
563
	return err;
564

            
565
    err = write_ppm (surface, fd);
566
    cairo_surface_destroy (surface);
567

            
568
    return err;
569
}
570
#else
571
static const char *
572
ps_convert (char **argv, int fd)
573
{
574
    return "compiled without PostScript support.";
575
}
576
#endif
577

            
578
static const char *
579
convert (char **argv, int fd)
580
{
581
    static const struct converter {
582
	const char *type;
583
	const char *(*func) (char **, int);
584
    } converters[] = {
585
	{ "cs", cs_convert },
586
	{ "pdf", pdf_convert },
587
	{ "ps", ps_convert },
588
	{ "svg", svg_convert },
589
	{ NULL, NULL }
590
    };
591
    const struct converter *converter = converters;
592
    char *type;
593

            
594
    type = strrchr (argv[0], '.');
595
    if (type == NULL)
596
	return "no file extension";
597
    type++;
598

            
599
    while (converter->type) {
600
	if (strcmp (type, converter->type) == 0)
601
	    return converter->func (argv, fd);
602
	converter++;
603
    }
604
    return "no converter";
605
}
606

            
607
#if CAN_RUN_AS_DAEMON
608
static int
609
_getline (int fd, char **linep, size_t *lenp)
610
{
611
    char *line;
612
    size_t len, i;
613
    ssize_t ret;
614

            
615
    line = *linep;
616
    if (line == NULL) {
617
	line = malloc (1024);
618
	if (line == NULL)
619
	    return -1;
620
	line[0] = '\0';
621
	len = 1024;
622
    } else
623
	len = *lenp;
624

            
625
    /* XXX simple, but ugly! */
626
    i = 0;
627
    do {
628
	if (i == len - 1) {
629
	    char *nline;
630

            
631
	    nline = realloc (line, len + 1024);
632
	    if (nline == NULL)
633
		goto out;
634

            
635
	    line = nline;
636
	    len += 1024;
637
	}
638

            
639
	ret = read (fd, line + i, 1);
640
	if (ret == -1 || ret == 0)
641
	    goto out;
642
    } while (line[i++] != '\n');
643

            
644
out:
645
    line[i] = '\0';
646
    *linep = line;
647
    *lenp = len;
648
    return i-1;
649
}
650

            
651
static int
652
split_line (char *line, char *argv[], int max_argc)
653
{
654
    int i = 0;
655

            
656
    max_argc--; /* leave one spare for the trailing NULL */
657

            
658
    argv[i++] = line;
659
    while (i < max_argc && (line = strchr (line, ' ')) != NULL) {
660
	*line++ = '\0';
661
	argv[i++] = line;
662
    }
663

            
664
    /* chomp the newline */
665
    line = strchr (argv[i-1], '\n');
666
    if (line != NULL)
667
	*line = '\0';
668

            
669
    argv[i] = NULL;
670

            
671
    return i;
672
}
673

            
674
static int
675
any2ppm_daemon_exists (void)
676
{
677
    struct stat st;
678
    int fd;
679
    char buf[80];
680
    int pid;
681
    int ret;
682

            
683
    if (stat (SOCKET_PATH, &st) < 0)
684
	return 0;
685

            
686
    fd = open (SOCKET_PATH ".pid", O_RDONLY);
687
    if (fd < 0)
688
	return 0;
689

            
690
    pid = 0;
691
    ret = read (fd, buf, sizeof (buf) - 1);
692
    if (ret > 0) {
693
	buf[ret] = '\0';
694
	pid = atoi (buf);
695
    }
696
    close (fd);
697

            
698
    return pid > 0 && kill (pid, 0) == 0;
699
}
700

            
701
static int
702
write_pid_file (void)
703
{
704
    int fd;
705
    char buf[80];
706
    int ret;
707

            
708
    fd = open (SOCKET_PATH ".pid", O_CREAT | O_TRUNC | O_WRONLY, 0666);
709
    if (fd < 0)
710
	return 0;
711

            
712
    ret = sprintf (buf, "%d\n", getpid ());
713
    ret = write (fd, buf, ret) == ret;
714
    close (fd);
715

            
716
    return ret;
717
}
718

            
719
static int
720
open_devnull_to_fd (int want_fd, int flags)
721
{
722
    int error;
723
    int got_fd;
724

            
725
    close (want_fd);
726

            
727
    got_fd = open("/dev/null", flags | O_CREAT, 0700);
728
    if (got_fd == -1)
729
        return -1;
730

            
731
    error = dup2 (got_fd, want_fd);
732
    close (got_fd);
733

            
734
    return error;
735
}
736

            
737
static int
738
daemonize (void)
739
{
740
    void (*oldhup) (int);
741

            
742
    /* Let the parent go. */
743
    switch (fork ()) {
744
    case -1: return -1;
745
    case 0: break;
746
    default: _exit (0);
747
    }
748

            
749
    /* Become session leader. */
750
    if (setsid () == -1)
751
	return -1;
752

            
753
    /* Refork to yield session leadership. */
754
    oldhup = signal (SIGHUP, SIG_IGN);
755

            
756
    switch (fork ()) {		/* refork to yield session leadership. */
757
    case -1: return -1;
758
    case 0: break;
759
    default: _exit (0);
760
    }
761

            
762
    signal (SIGHUP, oldhup);
763

            
764
    /* Establish stdio. */
765
    if (open_devnull_to_fd (0, O_RDONLY) == -1)
766
	return -1;
767
    if (open_devnull_to_fd (1, O_WRONLY | O_APPEND) == -1)
768
	return -1;
769
    if (dup2 (1, 2) == -1)
770
	return -1;
771

            
772
    return 0;
773
}
774

            
775
static const char *
776
any2ppm_daemon (void)
777
{
778
    int timeout = TIMEOUT;
779
    struct pollfd pfd;
780
    int sk, fd;
781
    long flags;
782
    struct sockaddr_un addr;
783
    char *line = NULL;
784
    size_t len = 0;
785

            
786
#ifdef SIGPIPE
787
    signal (SIGPIPE, SIG_IGN);
788
#endif
789

            
790
    /* XXX racy! */
791
    if (getenv ("ANY2PPM_FORCE") == NULL && any2ppm_daemon_exists ())
792
	return "any2ppm daemon already running";
793

            
794
    unlink (SOCKET_PATH);
795

            
796
    sk = socket (PF_UNIX, SOCK_STREAM, 0);
797
    if (sk == -1)
798
	return "unable to create socket";
799

            
800
    memset (&addr, 0, sizeof (addr));
801
    addr.sun_family = AF_UNIX;
802
    strcpy (addr.sun_path, SOCKET_PATH);
803
    if (bind (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
804
	close (sk);
805
	return "unable to bind socket";
806
    }
807

            
808
    flags = fcntl (sk, F_GETFL);
809
    if (flags == -1 || fcntl (sk, F_SETFL, flags | O_NONBLOCK) == -1) {
810
	close (sk);
811
	return "unable to set socket to non-blocking";
812
    }
813

            
814
    if (listen (sk, 5) == -1) {
815
	close (sk);
816
	return "unable to listen on socket";
817
    }
818

            
819
    /* ready for client connection - detach from parent/terminal */
820
    if (getenv ("ANY2PPM_NODAEMON") == NULL && daemonize () == -1) {
821
	close (sk);
822
	return "unable to detach from parent";
823
    }
824

            
825
    if (! write_pid_file ()) {
826
	close (sk);
827
	return "unable to write pid file";
828
    }
829

            
830
    if (getenv ("ANY2PPM_TIMEOUT") != NULL) {
831
	timeout = atoi (getenv ("ANY2PPM_TIMEOUT"));
832
	if (timeout == 0)
833
	    timeout = -1;
834
	if (timeout > 0)
835
	    timeout *= 1000; /* convert env (in seconds) to milliseconds */
836
    }
837

            
838
    pfd.fd = sk;
839
    pfd.events = POLLIN;
840
    pfd.revents = 0; /* valgrind */
841
    while (poll (&pfd, 1, timeout) > 0) {
842
	while ((fd = accept (sk, NULL, NULL)) != -1) {
843
	    if (_getline (fd, &line, &len) != -1) {
844
		char *argv[10];
845

            
846
		if (split_line (line, argv, ARRAY_LENGTH (argv)) > 0) {
847
		    const char *err;
848

            
849
		    err = convert (argv, fd);
850
		    if (err != NULL) {
851
			FILE *file = fopen (".any2ppm.errors", "a");
852
			if (file != NULL) {
853
			    fprintf (file,
854
				     "Failed to convert '%s': %s\n",
855
				     argv[0], err);
856
			    fclose (file);
857
			}
858
		    }
859
		}
860
	    }
861
	    close (fd);
862
	}
863
    }
864
    close (sk);
865
    unlink (SOCKET_PATH);
866
    unlink (SOCKET_PATH ".pid");
867

            
868
    free (line);
869
    return NULL;
870
}
871
#else
872
static const char *
873
any2ppm_daemon (void)
874
{
875
    return "daemon not compiled in.";
876
}
877
#endif
878

            
879
int
880
main (int argc, char **argv)
881
{
882
    const char *err;
883

            
884
#if CAIRO_CAN_TEST_PDF_SURFACE || CAIRO_CAN_TEST_SVG_SURFACE
885
#if GLIB_MAJOR_VERSION <= 2 && GLIB_MINOR_VERSION <= 34
886
    g_type_init ();
887
#endif
888
#endif
889

            
890
#if defined(_WIN32) && !defined (__CYGWIN__)
891
    _setmode (1, _O_BINARY);
892
#endif
893

            
894
    if (argc == 1)
895
	err = any2ppm_daemon ();
896
    else
897
	err = convert (argv + 1, 1);
898
    if (err != NULL) {
899
	fprintf (stderr, "Failed to run converter: %s\n", err);
900
	return EXIT_FAILURE;
901
    }
902

            
903
    return EXIT_SUCCESS;
904
}