mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2025-12-24 10:10:11 +01:00
Real applications that control their Drawable externally to Cairo are 'disadvantaged' by cairo-perf-trace when it creates a similar surface for each new instance of the same Drawable. The difficulty in maintaining one perf surface for every application surface is that the traces do not track lifetimes for the application surfaces, so we would just accumulate stale surfaces. The surface cache takes a different approach and returns the same surface for each active Drawable, and maintains a hold-over of the MRU 16 surfaces. This achieves 60-80% hit rate with firefox, which is probably as good as can be expected. Obviously for double-buffered applications we only every draw to freshly created surfaces (and Gtk+ bypasses cairo to do the final copy -- the ideal application would just use a push-group for double buffering, in which case we would capture and replay the entire expose event). To enable use of the surface cache whilst replaying use -c: ./cairo-perf-trace -c firefox-talos-gfx
867 lines
19 KiB
C
867 lines
19 KiB
C
/*
|
|
* Copyright © 2008 Chris Wilson
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software
|
|
* and its documentation for any purpose is hereby granted without
|
|
* fee, provided that the above copyright notice appear in all copies
|
|
* and that both that copyright notice and this permission notice
|
|
* appear in supporting documentation, and that the name of
|
|
* Chris Wilson not be used in advertising or publicity pertaining to
|
|
* distribution of the software without specific, written prior
|
|
* permission. Chris Wilson makes no representations about the
|
|
* suitability of this software for any purpose. It is provided "as
|
|
* is" without express or implied warranty.
|
|
*
|
|
* CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
|
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
* FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
|
|
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
|
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* Author: Chris Wilson <chris@chris-wilson.co.uk>
|
|
*
|
|
* Contributor(s):
|
|
* Carlos Garcia Campos <carlosgc@gnome.org>
|
|
*
|
|
* Adapted from pdf2png.c:
|
|
* Copyright © 2005 Red Hat, Inc.
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software
|
|
* and its documentation for any purpose is hereby granted without
|
|
* fee, provided that the above copyright notice appear in all copies
|
|
* and that both that copyright notice and this permission notice
|
|
* appear in supporting documentation, and that the name of
|
|
* Red Hat, Inc. not be used in advertising or publicity pertaining to
|
|
* distribution of the software without specific, written prior
|
|
* permission. Red Hat, Inc. makes no representations about the
|
|
* suitability of this software for any purpose. It is provided "as
|
|
* is" without express or implied warranty.
|
|
*
|
|
* RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
|
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
* FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
|
|
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
|
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* Author: Kristian Høgsberg <krh@redhat.com>
|
|
*/
|
|
|
|
#if HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#if HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <cairo.h>
|
|
#include <cairo-script-interpreter.h>
|
|
|
|
#if CAIRO_CAN_TEST_PDF_SURFACE
|
|
#include <poppler.h>
|
|
#endif
|
|
|
|
#if CAIRO_CAN_TEST_SVG_SURFACE
|
|
#include <librsvg/rsvg.h>
|
|
#include <librsvg/rsvg-cairo.h>
|
|
#endif
|
|
|
|
#if CAIRO_HAS_SPECTRE
|
|
#include <libspectre/spectre.h>
|
|
#endif
|
|
|
|
#if HAVE_UNISTD_H && HAVE_FCNTL_H && HAVE_SIGNAL_H && HAVE_SYS_STAT_H && HAVE_SYS_SOCKET_H && HAVE_SYS_POLL_H && HAVE_SYS_UN_H
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/un.h>
|
|
#include <errno.h>
|
|
|
|
#define SOCKET_PATH "./.any2ppm"
|
|
#define TIMEOUT 60000 /* 60 seconds */
|
|
|
|
#define CAN_RUN_AS_DAEMON 1
|
|
#endif
|
|
|
|
#define ARRAY_LENGTH(A) (sizeof (A) / sizeof (A[0]))
|
|
|
|
static int
|
|
_writen (int fd, char *buf, int len)
|
|
{
|
|
while (len) {
|
|
int ret;
|
|
|
|
ret = write (fd, buf, len);
|
|
if (ret == -1) {
|
|
int err = errno;
|
|
switch (err) {
|
|
case EINTR:
|
|
case EAGAIN:
|
|
continue;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
len -= ret;
|
|
buf += ret;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
_write (int fd,
|
|
char *buf, int maxlen, int buflen,
|
|
const unsigned char *src, int srclen)
|
|
{
|
|
if (buflen < 0)
|
|
return buflen;
|
|
|
|
while (srclen) {
|
|
int len;
|
|
|
|
len = buflen + srclen;
|
|
if (len > maxlen)
|
|
len = maxlen;
|
|
len -= buflen;
|
|
|
|
memcpy (buf + buflen, src, len);
|
|
buflen += len;
|
|
srclen -= len;
|
|
src += len;
|
|
|
|
if (buflen == maxlen) {
|
|
if (! _writen (fd, buf, buflen))
|
|
return -1;
|
|
|
|
buflen = 0;
|
|
}
|
|
}
|
|
|
|
return buflen;
|
|
}
|
|
|
|
static const char *
|
|
write_ppm (cairo_surface_t *surface, int fd)
|
|
{
|
|
char buf[4096];
|
|
cairo_format_t format;
|
|
const char *format_str;
|
|
const unsigned char *data;
|
|
int len;
|
|
int width, height, stride;
|
|
int i, j;
|
|
|
|
data = cairo_image_surface_get_data (surface);
|
|
height = cairo_image_surface_get_height (surface);
|
|
width = cairo_image_surface_get_width (surface);
|
|
stride = cairo_image_surface_get_stride (surface);
|
|
format = cairo_image_surface_get_format (surface);
|
|
if (format == CAIRO_FORMAT_ARGB32) {
|
|
/* see if we can convert to a standard ppm type and trim a few bytes */
|
|
const unsigned char *alpha = data;
|
|
for (j = height; j--; alpha += stride) {
|
|
for (i = 0; i < width; i++) {
|
|
if ((*(unsigned int *) (alpha+4*i) & 0xff000000) != 0xff000000)
|
|
goto done;
|
|
}
|
|
}
|
|
format = CAIRO_FORMAT_RGB24;
|
|
done: ;
|
|
}
|
|
|
|
switch (format) {
|
|
case CAIRO_FORMAT_ARGB32:
|
|
/* XXX need true alpha for svg */
|
|
format_str = "P7";
|
|
break;
|
|
case CAIRO_FORMAT_RGB24:
|
|
format_str = "P6";
|
|
break;
|
|
case CAIRO_FORMAT_A8:
|
|
format_str = "P5";
|
|
break;
|
|
case CAIRO_FORMAT_A1:
|
|
default:
|
|
return "unhandled image format";
|
|
}
|
|
|
|
len = sprintf (buf, "%s %d %d 255\n", format_str, width, height);
|
|
for (j = 0; j < height; j++) {
|
|
const unsigned int *row = (unsigned int *) (data + stride * j);
|
|
|
|
switch ((int) format) {
|
|
case CAIRO_FORMAT_ARGB32:
|
|
len = _write (fd,
|
|
buf, sizeof (buf), len,
|
|
(unsigned char *) row, 4 * width);
|
|
break;
|
|
case CAIRO_FORMAT_RGB24:
|
|
for (i = 0; i < width; i++) {
|
|
unsigned char rgb[3];
|
|
unsigned int p = *row++;
|
|
rgb[0] = (p & 0xff0000) >> 16;
|
|
rgb[1] = (p & 0x00ff00) >> 8;
|
|
rgb[2] = (p & 0x0000ff) >> 0;
|
|
len = _write (fd,
|
|
buf, sizeof (buf), len,
|
|
rgb, 3);
|
|
}
|
|
break;
|
|
case CAIRO_FORMAT_A8:
|
|
len = _write (fd,
|
|
buf, sizeof (buf), len,
|
|
(unsigned char *) row, width);
|
|
break;
|
|
}
|
|
if (len < 0)
|
|
return "write failed";
|
|
}
|
|
|
|
if (len && ! _writen (fd, buf, len))
|
|
return "write failed";
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
_create_image (void *closure,
|
|
cairo_content_t content,
|
|
double width, double height,
|
|
long uid)
|
|
{
|
|
cairo_surface_t **out = closure;
|
|
cairo_format_t format;
|
|
switch (content) {
|
|
case CAIRO_CONTENT_ALPHA:
|
|
format = CAIRO_FORMAT_A8;
|
|
break;
|
|
case CAIRO_CONTENT_COLOR:
|
|
format = CAIRO_FORMAT_RGB24;
|
|
break;
|
|
default:
|
|
case CAIRO_CONTENT_COLOR_ALPHA:
|
|
format = CAIRO_FORMAT_ARGB32;
|
|
break;
|
|
}
|
|
*out = cairo_image_surface_create (format, width, height);
|
|
return cairo_surface_reference (*out);
|
|
}
|
|
|
|
#if CAIRO_HAS_INTERPRETER
|
|
static const char *
|
|
_cairo_script_render_page (const char *filename,
|
|
cairo_surface_t **surface_out)
|
|
{
|
|
cairo_script_interpreter_t *csi;
|
|
cairo_surface_t *surface = NULL;
|
|
cairo_status_t status;
|
|
const cairo_script_interpreter_hooks_t hooks = {
|
|
.closure = &surface,
|
|
.surface_create = _create_image,
|
|
};
|
|
|
|
csi = cairo_script_interpreter_create ();
|
|
cairo_script_interpreter_install_hooks (csi, &hooks);
|
|
cairo_script_interpreter_run (csi, filename);
|
|
status = cairo_script_interpreter_destroy (csi);
|
|
if (surface == NULL) {
|
|
return "cairo-script interpreter failed";
|
|
}
|
|
|
|
if (status == CAIRO_STATUS_SUCCESS)
|
|
status = cairo_surface_status (surface);
|
|
if (status) {
|
|
cairo_surface_destroy (surface);
|
|
return cairo_status_to_string (status);
|
|
}
|
|
|
|
*surface_out = surface;
|
|
return NULL;
|
|
}
|
|
|
|
static const char *
|
|
cs_convert (char **argv, int fd)
|
|
{
|
|
const char *err;
|
|
cairo_surface_t *surface = NULL; /* silence compiler warning */
|
|
|
|
err = _cairo_script_render_page (argv[0], &surface);
|
|
if (err != NULL)
|
|
return err;
|
|
|
|
err = write_ppm (surface, fd);
|
|
cairo_surface_destroy (surface);
|
|
|
|
return err;
|
|
}
|
|
#else
|
|
static const char *
|
|
cs_convert (char **argv, int fd)
|
|
{
|
|
return "compiled without CairoScript support.";
|
|
}
|
|
#endif
|
|
|
|
#if CAIRO_CAN_TEST_PDF_SURFACE
|
|
/* adapted from pdf2png.c */
|
|
static const char *
|
|
_poppler_render_page (const char *filename,
|
|
const char *page_label,
|
|
cairo_surface_t **surface_out)
|
|
{
|
|
PopplerDocument *document;
|
|
PopplerPage *page;
|
|
double width, height;
|
|
GError *error = NULL;
|
|
gchar *absolute, *uri;
|
|
cairo_surface_t *surface;
|
|
cairo_t *cr;
|
|
cairo_status_t status;
|
|
|
|
if (g_path_is_absolute (filename)) {
|
|
absolute = g_strdup (filename);
|
|
} else {
|
|
gchar *dir = g_get_current_dir ();
|
|
absolute = g_build_filename (dir, filename, (gchar *) 0);
|
|
g_free (dir);
|
|
}
|
|
|
|
uri = g_filename_to_uri (absolute, NULL, &error);
|
|
g_free (absolute);
|
|
if (uri == NULL)
|
|
return error->message; /* XXX g_error_free (error) */
|
|
|
|
document = poppler_document_new_from_file (uri, NULL, &error);
|
|
g_free (uri);
|
|
if (document == NULL)
|
|
return error->message; /* XXX g_error_free (error) */
|
|
|
|
page = poppler_document_get_page_by_label (document, page_label);
|
|
g_object_unref (document);
|
|
if (page == NULL)
|
|
return "page not found";
|
|
|
|
poppler_page_get_size (page, &width, &height);
|
|
|
|
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
|
|
cr = cairo_create (surface);
|
|
|
|
poppler_page_render (page, cr);
|
|
g_object_unref (page);
|
|
|
|
cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
|
|
cairo_set_source_rgb (cr, 1., 1., 1.);
|
|
cairo_paint (cr);
|
|
|
|
status = cairo_status (cr);
|
|
cairo_destroy (cr);
|
|
|
|
if (status) {
|
|
cairo_surface_destroy (surface);
|
|
return cairo_status_to_string (status);
|
|
}
|
|
|
|
*surface_out = surface;
|
|
return NULL;
|
|
}
|
|
|
|
static const char *
|
|
pdf_convert (char **argv, int fd)
|
|
{
|
|
const char *err;
|
|
cairo_surface_t *surface = NULL; /* silence compiler warning */
|
|
|
|
err = _poppler_render_page (argv[0], argv[1], &surface);
|
|
if (err != NULL)
|
|
return err;
|
|
|
|
err = write_ppm (surface, fd);
|
|
cairo_surface_destroy (surface);
|
|
|
|
return err;
|
|
}
|
|
#else
|
|
static const char *
|
|
pdf_convert (char **argv, int fd)
|
|
{
|
|
return "compiled without PDF support.";
|
|
}
|
|
#endif
|
|
|
|
#if CAIRO_CAN_TEST_SVG_SURFACE
|
|
static const char *
|
|
_rsvg_render_page (const char *filename,
|
|
cairo_surface_t **surface_out)
|
|
{
|
|
RsvgHandle *handle;
|
|
RsvgDimensionData dimensions;
|
|
GError *error = NULL;
|
|
cairo_surface_t *surface;
|
|
cairo_t *cr;
|
|
cairo_status_t status;
|
|
|
|
handle = rsvg_handle_new_from_file (filename, &error);
|
|
if (handle == NULL)
|
|
return error->message; /* XXX g_error_free */
|
|
|
|
rsvg_handle_get_dimensions (handle, &dimensions);
|
|
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
|
dimensions.width,
|
|
dimensions.height);
|
|
cr = cairo_create (surface);
|
|
|
|
rsvg_handle_render_cairo (handle, cr);
|
|
g_object_unref (handle);
|
|
|
|
status = cairo_status (cr);
|
|
cairo_destroy (cr);
|
|
|
|
if (status) {
|
|
cairo_surface_destroy (surface);
|
|
return cairo_status_to_string (status);
|
|
}
|
|
|
|
*surface_out = surface;
|
|
return NULL;
|
|
}
|
|
|
|
static const char *
|
|
svg_convert (char **argv, int fd)
|
|
{
|
|
const char *err;
|
|
cairo_surface_t *surface = NULL; /* silence compiler warning */
|
|
|
|
err = _rsvg_render_page (argv[0], &surface);
|
|
if (err != NULL)
|
|
return err;
|
|
|
|
err = write_ppm (surface, fd);
|
|
cairo_surface_destroy (surface);
|
|
|
|
return err;
|
|
}
|
|
#else
|
|
static const char *
|
|
svg_convert (char **argv, int fd)
|
|
{
|
|
return "compiled without SVG support.";
|
|
}
|
|
#endif
|
|
|
|
#if CAIRO_HAS_SPECTRE
|
|
static const char *
|
|
_spectre_render_page (const char *filename,
|
|
const char *page_label,
|
|
cairo_surface_t **surface_out)
|
|
{
|
|
static const cairo_user_data_key_t key;
|
|
|
|
SpectreDocument *document;
|
|
SpectreStatus status;
|
|
int width, height, stride;
|
|
unsigned char *pixels;
|
|
cairo_surface_t *surface;
|
|
|
|
document = spectre_document_new ();
|
|
spectre_document_load (document, filename);
|
|
status = spectre_document_status (document);
|
|
if (status) {
|
|
spectre_document_free (document);
|
|
return spectre_status_to_string (status);
|
|
}
|
|
|
|
if (page_label) {
|
|
SpectrePage *page;
|
|
SpectreRenderContext *rc;
|
|
|
|
page = spectre_document_get_page_by_label (document, page_label);
|
|
spectre_document_free (document);
|
|
if (page == NULL)
|
|
return "page not found";
|
|
|
|
spectre_page_get_size (page, &width, &height);
|
|
rc = spectre_render_context_new ();
|
|
spectre_render_context_set_page_size (rc, width, height);
|
|
spectre_page_render (page, rc, &pixels, &stride);
|
|
spectre_render_context_free (rc);
|
|
status = spectre_page_status (page);
|
|
spectre_page_free (page);
|
|
if (status) {
|
|
free (pixels);
|
|
return spectre_status_to_string (status);
|
|
}
|
|
} else {
|
|
spectre_document_get_page_size (document, &width, &height);
|
|
spectre_document_render (document, &pixels, &stride);
|
|
spectre_document_free (document);
|
|
}
|
|
|
|
surface = cairo_image_surface_create_for_data (pixels,
|
|
CAIRO_FORMAT_RGB24,
|
|
width, height,
|
|
stride);
|
|
cairo_surface_set_user_data (surface, &key,
|
|
pixels, (cairo_destroy_func_t) free);
|
|
*surface_out = surface;
|
|
return NULL;
|
|
}
|
|
|
|
static const char *
|
|
ps_convert (char **argv, int fd)
|
|
{
|
|
const char *err;
|
|
cairo_surface_t *surface = NULL; /* silence compiler warning */
|
|
|
|
err = _spectre_render_page (argv[0], argv[1], &surface);
|
|
if (err != NULL)
|
|
return err;
|
|
|
|
err = write_ppm (surface, fd);
|
|
cairo_surface_destroy (surface);
|
|
|
|
return err;
|
|
}
|
|
#else
|
|
static const char *
|
|
ps_convert (char **argv, int fd)
|
|
{
|
|
return "compiled without PostScript support.";
|
|
}
|
|
#endif
|
|
|
|
static const char *
|
|
convert (char **argv, int fd)
|
|
{
|
|
static const struct converter {
|
|
const char *type;
|
|
const char *(*func) (char **, int);
|
|
} converters[] = {
|
|
{ "cs", cs_convert },
|
|
{ "pdf", pdf_convert },
|
|
{ "ps", ps_convert },
|
|
{ "svg", svg_convert },
|
|
{ NULL, NULL }
|
|
};
|
|
const struct converter *converter = converters;
|
|
char *type;
|
|
|
|
type = strrchr (argv[0], '.');
|
|
if (type == NULL)
|
|
return "no file extension";
|
|
type++;
|
|
|
|
while (converter->type) {
|
|
if (strcmp (type, converter->type) == 0)
|
|
return converter->func (argv, fd);
|
|
converter++;
|
|
}
|
|
return "no converter";
|
|
}
|
|
|
|
#if CAN_RUN_AS_DAEMON
|
|
static int
|
|
_getline (int fd, char **linep, size_t *lenp)
|
|
{
|
|
char *line;
|
|
size_t len, i;
|
|
ssize_t ret;
|
|
|
|
line = *linep;
|
|
if (line == NULL) {
|
|
line = malloc (1024);
|
|
if (line == NULL)
|
|
return -1;
|
|
line[0] = '\0';
|
|
len = 1024;
|
|
} else
|
|
len = *lenp;
|
|
|
|
/* XXX simple, but ugly! */
|
|
i = 0;
|
|
do {
|
|
if (i == len - 1) {
|
|
char *nline;
|
|
|
|
nline = realloc (line, len + 1024);
|
|
if (nline == NULL)
|
|
goto out;
|
|
|
|
line = nline;
|
|
len += 1024;
|
|
}
|
|
|
|
ret = read (fd, line + i, 1);
|
|
if (ret == -1 || ret == 0)
|
|
goto out;
|
|
} while (line[i++] != '\n');
|
|
|
|
out:
|
|
line[i] = '\0';
|
|
*linep = line;
|
|
*lenp = len;
|
|
return i-1;
|
|
}
|
|
|
|
static int
|
|
split_line (char *line, char *argv[], int max_argc)
|
|
{
|
|
int i = 0;
|
|
|
|
max_argc--; /* leave one spare for the trailing NULL */
|
|
|
|
argv[i++] = line;
|
|
while (i < max_argc && (line = strchr (line, ' ')) != NULL) {
|
|
*line++ = '\0';
|
|
argv[i++] = line;
|
|
}
|
|
|
|
/* chomp the newline */
|
|
line = strchr (argv[i-1], '\n');
|
|
if (line != NULL)
|
|
*line = '\0';
|
|
|
|
argv[i] = NULL;
|
|
|
|
return i;
|
|
}
|
|
|
|
static int
|
|
any2ppm_daemon_exists (void)
|
|
{
|
|
struct stat st;
|
|
int fd;
|
|
char buf[80];
|
|
int pid;
|
|
int ret;
|
|
|
|
if (stat (SOCKET_PATH, &st) < 0)
|
|
return 0;
|
|
|
|
fd = open (SOCKET_PATH ".pid", O_RDONLY);
|
|
if (fd < 0)
|
|
return 0;
|
|
|
|
pid = 0;
|
|
ret = read (fd, buf, sizeof (buf) - 1);
|
|
if (ret > 0) {
|
|
buf[ret] = '\0';
|
|
pid = atoi (buf);
|
|
}
|
|
close (fd);
|
|
|
|
return pid > 0 && kill (pid, 0) == 0;
|
|
}
|
|
|
|
static int
|
|
write_pid_file (void)
|
|
{
|
|
int fd;
|
|
char buf[80];
|
|
int ret;
|
|
|
|
fd = open (SOCKET_PATH ".pid", O_CREAT | O_TRUNC | O_WRONLY, 0666);
|
|
if (fd < 0)
|
|
return 0;
|
|
|
|
ret = sprintf (buf, "%d\n", getpid ());
|
|
ret = write (fd, buf, ret) == ret;
|
|
close (fd);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
open_devnull_to_fd (int want_fd, int flags)
|
|
{
|
|
int error;
|
|
int got_fd;
|
|
|
|
close (want_fd);
|
|
|
|
got_fd = open("/dev/null", flags | O_CREAT, 0700);
|
|
if (got_fd == -1)
|
|
return -1;
|
|
|
|
error = dup2 (got_fd, want_fd);
|
|
close (got_fd);
|
|
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
daemonize (void)
|
|
{
|
|
void (*oldhup) (int);
|
|
|
|
/* Let the parent go. */
|
|
switch (fork ()) {
|
|
case -1: return -1;
|
|
case 0: break;
|
|
default: _exit (0);
|
|
}
|
|
|
|
/* Become session leader. */
|
|
if (setsid () == -1)
|
|
return -1;
|
|
|
|
/* Refork to yield session leadership. */
|
|
oldhup = signal (SIGHUP, SIG_IGN);
|
|
|
|
switch (fork ()) { /* refork to yield session leadership. */
|
|
case -1: return -1;
|
|
case 0: break;
|
|
default: _exit (0);
|
|
}
|
|
|
|
signal (SIGHUP, oldhup);
|
|
|
|
/* Establish stdio. */
|
|
if (open_devnull_to_fd (0, O_RDONLY) == -1)
|
|
return -1;
|
|
if (open_devnull_to_fd (1, O_WRONLY | O_APPEND) == -1)
|
|
return -1;
|
|
if (dup2 (1, 2) == -1)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const char *
|
|
any2ppm_daemon (void)
|
|
{
|
|
int timeout = TIMEOUT;
|
|
struct pollfd pfd;
|
|
int sk, fd;
|
|
long flags;
|
|
struct sockaddr_un addr;
|
|
char *line = NULL;
|
|
size_t len = 0;
|
|
|
|
#ifdef SIGPIPE
|
|
signal (SIGPIPE, SIG_IGN);
|
|
#endif
|
|
|
|
/* XXX racy! */
|
|
if (getenv ("ANY2PPM_FORCE") == NULL && any2ppm_daemon_exists ())
|
|
return "any2ppm daemon already running";
|
|
|
|
unlink (SOCKET_PATH);
|
|
|
|
sk = socket (PF_UNIX, SOCK_STREAM, 0);
|
|
if (sk == -1)
|
|
return "unable to create socket";
|
|
|
|
memset (&addr, 0, sizeof (addr));
|
|
addr.sun_family = AF_UNIX;
|
|
strcpy (addr.sun_path, SOCKET_PATH);
|
|
if (bind (sk, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
|
|
close (sk);
|
|
return "unable to bind socket";
|
|
}
|
|
|
|
flags = fcntl (sk, F_GETFL);
|
|
if (flags == -1 || fcntl (sk, F_SETFL, flags | O_NONBLOCK) == -1) {
|
|
close (sk);
|
|
return "unable to set socket to non-blocking";
|
|
}
|
|
|
|
if (listen (sk, 5) == -1) {
|
|
close (sk);
|
|
return "unable to listen on socket";
|
|
}
|
|
|
|
/* ready for client connection - detach from parent/terminal */
|
|
if (getenv ("ANY2PPM_NODAEMON") == NULL && daemonize () == -1) {
|
|
close (sk);
|
|
return "unable to detach from parent";
|
|
}
|
|
|
|
if (! write_pid_file ()) {
|
|
close (sk);
|
|
return "unable to write pid file";
|
|
}
|
|
|
|
if (getenv ("ANY2PPM_TIMEOUT") != NULL) {
|
|
timeout = atoi (getenv ("ANY2PPM_TIMEOUT"));
|
|
if (timeout == 0)
|
|
timeout = -1;
|
|
if (timeout > 0)
|
|
timeout *= 1000; /* convert env (in seconds) to milliseconds */
|
|
}
|
|
|
|
pfd.fd = sk;
|
|
pfd.events = POLLIN;
|
|
pfd.revents = 0; /* valgrind */
|
|
while (poll (&pfd, 1, timeout) > 0) {
|
|
while ((fd = accept (sk, NULL, NULL)) != -1) {
|
|
if (_getline (fd, &line, &len) != -1) {
|
|
char *argv[10];
|
|
|
|
if (split_line (line, argv, ARRAY_LENGTH (argv)) > 0) {
|
|
const char *err;
|
|
|
|
err = convert (argv, fd);
|
|
if (err != NULL) {
|
|
FILE *file = fopen (".any2ppm.errors", "a");
|
|
if (file != NULL) {
|
|
fprintf (file,
|
|
"Failed to convert '%s': %s\n",
|
|
argv[0], err);
|
|
fclose (file);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
close (fd);
|
|
}
|
|
}
|
|
close (sk);
|
|
unlink (SOCKET_PATH);
|
|
unlink (SOCKET_PATH ".pid");
|
|
|
|
free (line);
|
|
return NULL;
|
|
}
|
|
#else
|
|
static const char *
|
|
any2ppm_daemon (void)
|
|
{
|
|
return "daemon not compiled in.";
|
|
}
|
|
#endif
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
const char *err;
|
|
|
|
#if CAIRO_CAN_TEST_PDF_SURFACE || CAIRO_CAN_TEST_SVG_SURFACE
|
|
g_type_init ();
|
|
#endif
|
|
|
|
#if CAIRO_CAN_TEST_SVG_SURFACE
|
|
rsvg_init ();
|
|
rsvg_set_default_dpi (72.0);
|
|
#endif
|
|
|
|
if (argc == 1)
|
|
err = any2ppm_daemon ();
|
|
else
|
|
err = convert (argv + 1, 1);
|
|
if (err != NULL) {
|
|
fprintf (stderr, "Failed to run converter: %s\n", err);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|