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
#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
91
#include <signal.h>
92
#include <sys/stat.h>
93
#include <sys/socket.h>
94
#include <sys/un.h>
95

            
96
#if defined(HAVE_POLL_H)
97
#include <poll.h>
98
#elif defined(HAVE_SYS_POLL_H)
99
#include <sys/poll.h>
100
#endif
101

            
102
#define SOCKET_PATH "./.any2ppm"
103
#define TIMEOUT 60000 /* 60 seconds */
104

            
105
#if HAVE_FORK
106
#define CAN_RUN_AS_DAEMON 1
107
#endif
108
#endif
109

            
110
#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
111

            
112
static int
113
_cairo_writen (int fd, char *buf, int len)
114
{
115
    while (len) {
116
	int ret;
117

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

            
133
    return 1;
134
}
135

            
136
static int
137
_cairo_write (int fd,
138
	char *buf, int maxlen, int buflen,
139
	const unsigned char *src, int srclen)
140
{
141
    if (buflen < 0)
142
	return buflen;
143

            
144
    while (srclen) {
145
	int len;
146

            
147
	len = buflen + srclen;
148
	if (len > maxlen)
149
	    len = maxlen;
150
	len -= buflen;
151

            
152
	memcpy (buf + buflen, src, len);
153
	buflen += len;
154
	srclen -= len;
155
	src += len;
156

            
157
	if (buflen == maxlen) {
158
	    if (! _cairo_writen (fd, buf, buflen))
159
		return -1;
160

            
161
	    buflen = 0;
162
	}
163
    }
164

            
165
    return buflen;
166
}
167

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

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

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

            
218
    len = sprintf (buf, "%s %d %d 255\n", format_str, width, height);
219
    for (j = 0; j < height; j++) {
220
	const unsigned int *row = (unsigned int *) (data + stride * j);
221

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

            
250
    if (len && ! _cairo_writen (fd, buf, len))
251
	return "write failed";
252

            
253
    return NULL;
254
}
255

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

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

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

            
309
    if (status == CAIRO_STATUS_SUCCESS)
310
	status = cairo_surface_status (surface);
311
    if (status) {
312
	cairo_surface_destroy (surface);
313
	return cairo_status_to_string (status);
314
    }
315

            
316
    *surface_out = surface;
317
    return NULL;
318
}
319

            
320
static const char *
321
cs_convert (char **argv, int fd)
322
{
323
    const char *err;
324
    cairo_surface_t *surface = NULL; /* silence compiler warning */
325

            
326
    err = _cairo_script_render_page (argv[0], &surface);
327
    if (err != NULL)
328
	return err;
329

            
330
    err = write_ppm (surface, fd);
331
    cairo_surface_destroy (surface);
332

            
333
    return err;
334
}
335
#else
336
static const char *
337
cs_convert (char **argv, int fd)
338
{
339
    return "compiled without CairoScript support.";
340
}
341
#endif
342

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

            
359
    if (g_path_is_absolute (filename)) {
360
	absolute = g_strdup (filename);
361
    } else {
362
	gchar *dir = g_get_current_dir ();
363
	absolute = g_build_filename (dir, filename, (gchar *) 0);
364
	g_free (dir);
365
    }
366

            
367
    uri = g_filename_to_uri (absolute, NULL, &error);
368
    g_free (absolute);
369
    if (uri == NULL)
370
	return error->message; /* XXX g_error_free (error) */
371

            
372
    document = poppler_document_new_from_file (uri, NULL, &error);
373
    g_free (uri);
374
    if (document == NULL)
375
	return error->message; /* XXX g_error_free (error) */
376

            
377
    page = poppler_document_get_page_by_label (document, page_label);
378
    g_object_unref (document);
379
    if (page == NULL)
380
	return "page not found";
381

            
382
    poppler_page_get_size (page, &width, &height);
383

            
384
    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
385
    cr = cairo_create (surface);
386

            
387
    cairo_set_source_rgb (cr, 1., 1., 1.);
388
    cairo_paint (cr);
389
    cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA);
390

            
391
    poppler_page_render (page, cr);
392
    g_object_unref (page);
393

            
394
    cairo_pop_group_to_source (cr);
395
    cairo_paint (cr);
396

            
397
    status = cairo_status (cr);
398
    cairo_destroy (cr);
399

            
400
    if (status) {
401
	cairo_surface_destroy (surface);
402
	return  cairo_status_to_string (status);
403
    }
404

            
405
    *surface_out = surface;
406
    return NULL;
407
}
408

            
409
static const char *
410
pdf_convert (char **argv, int fd)
411
{
412
    const char *err;
413
    cairo_surface_t *surface = NULL; /* silence compiler warning */
414

            
415
    err = _poppler_render_page (argv[0], argv[1], &surface);
416
    if (err != NULL)
417
	return err;
418

            
419
    err = write_ppm (surface, fd);
420
    cairo_surface_destroy (surface);
421

            
422
    return err;
423
}
424
#else
425
static const char *
426
pdf_convert (char **argv, int fd)
427
{
428
    return "compiled without PDF support.";
429
}
430
#endif
431

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

            
444
    handle = rsvg_handle_new_from_file (filename, &error);
445
    if (handle == NULL)
446
	return error->message; /* XXX g_error_free */
447

            
448
    rsvg_handle_set_dpi (handle, 72.0);
449
    rsvg_handle_get_dimensions (handle, &dimensions);
450
    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
451
					  dimensions.width,
452
					  dimensions.height);
453
    cr = cairo_create (surface);
454

            
455
    rsvg_handle_render_cairo (handle, cr);
456
    g_object_unref (handle);
457

            
458
    status = cairo_status (cr);
459
    cairo_destroy (cr);
460

            
461
    if (status) {
462
	cairo_surface_destroy (surface);
463
	return  cairo_status_to_string (status);
464
    }
465

            
466
    *surface_out = surface;
467
    return NULL;
468
}
469

            
470
static const char *
471
svg_convert (char **argv, int fd)
472
{
473
    const char *err;
474
    cairo_surface_t *surface = NULL; /* silence compiler warning */
475

            
476
    err = _rsvg_render_page (argv[0], &surface);
477
    if (err != NULL)
478
	return err;
479

            
480
    err = write_ppm (surface, fd);
481
    cairo_surface_destroy (surface);
482

            
483
    return err;
484
}
485
#else
486
static const char *
487
svg_convert (char **argv, int fd)
488
{
489
    return "compiled without SVG support.";
490
}
491
#endif
492

            
493
#if CAIRO_HAS_SPECTRE
494
static const char *
495
_spectre_render_page (const char *filename,
496
		      const char *page_label,
497
		      cairo_surface_t **surface_out)
498
{
499
    static const cairo_user_data_key_t key;
500

            
501
    SpectreDocument *document;
502
    SpectreStatus status;
503
    int width, height, stride;
504
    unsigned char *pixels;
505
    cairo_surface_t *surface;
506

            
507
    document = spectre_document_new ();
508
    spectre_document_load (document, filename);
509
    status = spectre_document_status (document);
510
    if (status) {
511
	spectre_document_free (document);
512
	return spectre_status_to_string (status);
513
    }
514

            
515
    if (page_label) {
516
	SpectrePage *page;
517
	SpectreRenderContext *rc;
518

            
519
	page = spectre_document_get_page_by_label (document, page_label);
520
	spectre_document_free (document);
521
	if (page == NULL)
522
	    return "page not found";
523

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

            
541
    surface = cairo_image_surface_create_for_data (pixels,
542
						   CAIRO_FORMAT_RGB24,
543
						   width, height,
544
						   stride);
545
    cairo_surface_set_user_data (surface, &key,
546
				 pixels, (cairo_destroy_func_t) free);
547
    *surface_out = surface;
548
    return NULL;
549
}
550

            
551
static const char *
552
ps_convert (char **argv, int fd)
553
{
554
    const char *err;
555
    cairo_surface_t *surface = NULL; /* silence compiler warning */
556

            
557
    err = _spectre_render_page (argv[0], argv[1], &surface);
558
    if (err != NULL)
559
	return err;
560

            
561
    err = write_ppm (surface, fd);
562
    cairo_surface_destroy (surface);
563

            
564
    return err;
565
}
566
#else
567
static const char *
568
ps_convert (char **argv, int fd)
569
{
570
    return "compiled without PostScript support.";
571
}
572
#endif
573

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

            
590
    type = strrchr (argv[0], '.');
591
    if (type == NULL)
592
	return "no file extension";
593
    type++;
594

            
595
    while (converter->type) {
596
	if (strcmp (type, converter->type) == 0)
597
	    return converter->func (argv, fd);
598
	converter++;
599
    }
600
    return "no converter";
601
}
602

            
603
#if CAN_RUN_AS_DAEMON
604
static int
605
_getline (int fd, char **linep, size_t *lenp)
606
{
607
    char *line;
608
    size_t len, i;
609
    ssize_t ret;
610

            
611
    line = *linep;
612
    if (line == NULL) {
613
	line = malloc (1024);
614
	if (line == NULL)
615
	    return -1;
616
	line[0] = '\0';
617
	len = 1024;
618
    } else
619
	len = *lenp;
620

            
621
    /* XXX simple, but ugly! */
622
    i = 0;
623
    do {
624
	if (i == len - 1) {
625
	    char *nline;
626

            
627
	    nline = realloc (line, len + 1024);
628
	    if (nline == NULL)
629
		goto out;
630

            
631
	    line = nline;
632
	    len += 1024;
633
	}
634

            
635
	ret = read (fd, line + i, 1);
636
	if (ret == -1 || ret == 0)
637
	    goto out;
638
    } while (line[i++] != '\n');
639

            
640
out:
641
    line[i] = '\0';
642
    *linep = line;
643
    *lenp = len;
644
    return i-1;
645
}
646

            
647
static int
648
split_line (char *line, char *argv[], int max_argc)
649
{
650
    int i = 0;
651

            
652
    max_argc--; /* leave one spare for the trailing NULL */
653

            
654
    argv[i++] = line;
655
    while (i < max_argc && (line = strchr (line, ' ')) != NULL) {
656
	*line++ = '\0';
657
	argv[i++] = line;
658
    }
659

            
660
    /* chomp the newline */
661
    line = strchr (argv[i-1], '\n');
662
    if (line != NULL)
663
	*line = '\0';
664

            
665
    argv[i] = NULL;
666

            
667
    return i;
668
}
669

            
670
static int
671
any2ppm_daemon_exists (void)
672
{
673
    struct stat st;
674
    int fd;
675
    char buf[80];
676
    int pid;
677
    int ret;
678

            
679
    if (stat (SOCKET_PATH, &st) < 0)
680
	return 0;
681

            
682
    fd = open (SOCKET_PATH ".pid", O_RDONLY);
683
    if (fd < 0)
684
	return 0;
685

            
686
    pid = 0;
687
    ret = read (fd, buf, sizeof (buf) - 1);
688
    if (ret > 0) {
689
	buf[ret] = '\0';
690
	pid = atoi (buf);
691
    }
692
    close (fd);
693

            
694
    return pid > 0 && kill (pid, 0) == 0;
695
}
696

            
697
static int
698
write_pid_file (void)
699
{
700
    int fd;
701
    char buf[80];
702
    int ret;
703

            
704
    fd = open (SOCKET_PATH ".pid", O_CREAT | O_TRUNC | O_WRONLY, 0666);
705
    if (fd < 0)
706
	return 0;
707

            
708
    ret = sprintf (buf, "%d\n", getpid ());
709
    ret = write (fd, buf, ret) == ret;
710
    close (fd);
711

            
712
    return ret;
713
}
714

            
715
static int
716
open_devnull_to_fd (int want_fd, int flags)
717
{
718
    int error;
719
    int got_fd;
720

            
721
    close (want_fd);
722

            
723
    got_fd = open("/dev/null", flags | O_CREAT, 0700);
724
    if (got_fd == -1)
725
        return -1;
726

            
727
    error = dup2 (got_fd, want_fd);
728
    close (got_fd);
729

            
730
    return error;
731
}
732

            
733
static int
734
daemonize (void)
735
{
736
    void (*oldhup) (int);
737

            
738
    /* Let the parent go. */
739
    switch (fork ()) {
740
    case -1: return -1;
741
    case 0: break;
742
    default: _exit (0);
743
    }
744

            
745
    /* Become session leader. */
746
    if (setsid () == -1)
747
	return -1;
748

            
749
    /* Refork to yield session leadership. */
750
    oldhup = signal (SIGHUP, SIG_IGN);
751

            
752
    switch (fork ()) {		/* refork to yield session leadership. */
753
    case -1: return -1;
754
    case 0: break;
755
    default: _exit (0);
756
    }
757

            
758
    signal (SIGHUP, oldhup);
759

            
760
    /* Establish stdio. */
761
    if (open_devnull_to_fd (0, O_RDONLY) == -1)
762
	return -1;
763
    if (open_devnull_to_fd (1, O_WRONLY | O_APPEND) == -1)
764
	return -1;
765
    if (dup2 (1, 2) == -1)
766
	return -1;
767

            
768
    return 0;
769
}
770

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

            
782
#ifdef SIGPIPE
783
    signal (SIGPIPE, SIG_IGN);
784
#endif
785

            
786
    /* XXX racy! */
787
    if (getenv ("ANY2PPM_FORCE") == NULL && any2ppm_daemon_exists ())
788
	return "any2ppm daemon already running";
789

            
790
    unlink (SOCKET_PATH);
791

            
792
    sk = socket (PF_UNIX, SOCK_STREAM, 0);
793
    if (sk == -1)
794
	return "unable to create socket";
795

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

            
804
    flags = fcntl (sk, F_GETFL);
805
    if (flags == -1 || fcntl (sk, F_SETFL, flags | O_NONBLOCK) == -1) {
806
	close (sk);
807
	return "unable to set socket to non-blocking";
808
    }
809

            
810
    if (listen (sk, 5) == -1) {
811
	close (sk);
812
	return "unable to listen on socket";
813
    }
814

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

            
821
    if (! write_pid_file ()) {
822
	close (sk);
823
	return "unable to write pid file";
824
    }
825

            
826
    if (getenv ("ANY2PPM_TIMEOUT") != NULL) {
827
	timeout = atoi (getenv ("ANY2PPM_TIMEOUT"));
828
	if (timeout == 0)
829
	    timeout = -1;
830
	if (timeout > 0)
831
	    timeout *= 1000; /* convert env (in seconds) to milliseconds */
832
    }
833

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

            
842
		if (split_line (line, argv, ARRAY_LENGTH (argv)) > 0) {
843
		    const char *err;
844

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

            
864
    free (line);
865
    return NULL;
866
}
867
#else
868
static const char *
869
any2ppm_daemon (void)
870
{
871
    return "daemon not compiled in.";
872
}
873
#endif
874

            
875
int
876
main (int argc, char **argv)
877
{
878
    const char *err;
879

            
880
#if CAIRO_CAN_TEST_PDF_SURFACE || CAIRO_CAN_TEST_SVG_SURFACE
881
#if GLIB_MAJOR_VERSION <= 2 && GLIB_MINOR_VERSION <= 34
882
    g_type_init ();
883
#endif
884
#endif
885

            
886
#if defined(_WIN32) && !defined (__CYGWIN__)
887
    _setmode (1, _O_BINARY);
888
#endif
889

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

            
899
    return EXIT_SUCCESS;
900
}