projects/xwrited

changeset 0:52694b49dcc4

Initial revision
author Guido Berhoerster <guido+xwrited@berhoerster.name>
date Sun Apr 27 23:07:51 2014 +0200 (2014-04-27)
parents
children 0907cc7064d4
files Makefile deps.sed main.c po/POTFILES.in po/de.po xwrited-debug.c xwrited-debug.h xwrited-unique.c xwrited-unique.h xwrited-utmp-utempter.c xwrited-utmp-utmpx.c xwrited-utmp.h xwrited.desktop.in
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/Makefile	Sun Apr 27 23:07:51 2014 +0200
     1.3 @@ -0,0 +1,135 @@
     1.4 +#
     1.5 +# Copyright (C) 2011 Guido Berhoerster <guido+xwrited@berhoerster.name>
     1.6 +#
     1.7 +# Permission is hereby granted, free of charge, to any person obtaining
     1.8 +# a copy of this software and associated documentation files (the
     1.9 +# "Software"), to deal in the Software without restriction, including
    1.10 +# without limitation the rights to use, copy, modify, merge, publish,
    1.11 +# distribute, sublicense, and/or sell copies of the Software, and to
    1.12 +# permit persons to whom the Software is furnished to do so, subject to
    1.13 +# the following conditions:
    1.14 +#
    1.15 +# The above copyright notice and this permission notice shall be included
    1.16 +# in all copies or substantial portions of the Software.
    1.17 +#
    1.18 +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    1.19 +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    1.20 +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    1.21 +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    1.22 +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    1.23 +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    1.24 +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    1.25 +#
    1.26 +
    1.27 +PACKAGE =	xwrited
    1.28 +APP_NAME =	org.guido-berhoerster.code.xwrited
    1.29 +VERSION =	1
    1.30 +DISTNAME :=	$(PACKAGE)-$(VERSION)
    1.31 +
    1.32 +# gcc, clang, icc
    1.33 +MAKEDEPEND.c =	$(CC) -MM $(CFLAGS) $(CPPFLAGS)
    1.34 +# Sun/Solaris Studio
    1.35 +#MAKEDEPEND.c =	$(CC) -xM1 $(CFLAGS) $(CPPFLAGS)
    1.36 +# X makedepend
    1.37 +#MAKEDEPEND.c =	makedepend -f- -Y -- $(CFLAGS) $(CPPFLAGS) --
    1.38 +INSTALL :=	install
    1.39 +INSTALL.exec :=	$(INSTALL) -D -m 0755
    1.40 +INSTALL.data :=	$(INSTALL) -D -m 0644
    1.41 +PAX :=		pax
    1.42 +GZIP :=		gzip
    1.43 +SED :=		sed
    1.44 +PASTE :=	paste
    1.45 +MSGFMT :=	msgfmt
    1.46 +INTLTOOL_UPDATE := intltool-update
    1.47 +INTLTOOL_MERGE := intltool-merge
    1.48 +
    1.49 +DESTDIR ?=
    1.50 +prefix ?=	/usr/local
    1.51 +bindir ?=	$(prefix)/bin
    1.52 +datadir ?=	$(prefix)/share
    1.53 +localedir ?=	$(datadir)/locale
    1.54 +sysconfdir ?=	/etc
    1.55 +xdgautostartdir ?= $(sysconfdir)/xdg/autostart
    1.56 +
    1.57 +OS_NAME :=	$(shell uname -s)
    1.58 +
    1.59 +PKGCONFIG_LIBS :=	dbus-1 glib-2.0 dbus-glib-1 libnotify
    1.60 +EXTRA_LIBS :=
    1.61 +
    1.62 +ifeq ($(OS_NAME),Linux)
    1.63 +    OBJS_UTMP :=	xwrited-utmp-utempter.o
    1.64 +    EXTRA_LIBS +=	-lutempter
    1.65 +else ifeq ($(OS_NAME),FreeBSD)
    1.66 +    OBJS_UTMP :=	xwrited-utmp-utempter.o
    1.67 +    EXTRA_LIBS +=	-lutempter
    1.68 +else
    1.69 +    OBJS_UTMP :=	xwrited-utmp-utmpx.o
    1.70 +endif
    1.71 +
    1.72 +OBJS =		main.o xwrited-debug.o xwrited-unique.o $(OBJS_UTMP)
    1.73 +AUTOSTART_FILE = $(PACKAGE).desktop
    1.74 +MOFILES :=	$(patsubst %.po,%.mo,$(wildcard po/*.po))
    1.75 +POTFILE =	po/$(PACKAGE).pot
    1.76 +POSRCS :=	$(shell $(SED) -e 's/\#.*//' -e '/^[ \t]*$$/d' \
    1.77 +		-e 's/^\[[^]]*\]//' po/POTFILES.in | $(PASTE) -s -d ' ')
    1.78 +CPPFLAGS := 	$(CPPFLAGS_EXTRA) \
    1.79 +		$(CPPFLAGS_LIBUTEMPTER) \
    1.80 +		$(shell pkg-config --cflags $(PKGCONFIG_LIBS)) \
    1.81 +		-DPACKAGE="\"$(PACKAGE)\"" \
    1.82 +		-DAPP_NAME=\"$(APP_NAME)\" \
    1.83 +		-DVERSION=\"$(VERSION)\" \
    1.84 +		-DLOCALEDIR="\"$(localedir)\"" \
    1.85 +		-DG_LOG_DOMAIN=\"$(PACKAGE)\"
    1.86 +LDLIBS :=	$(EXTRA_LIBS) \
    1.87 +		$(shell pkg-config --libs $(PKGCONFIG_LIBS))
    1.88 +
    1.89 +.DEFAULT_TARGET = all
    1.90 +
    1.91 +.PHONY: all clean clobber dist install
    1.92 +
    1.93 +all: $(PACKAGE) $(MOFILES) $(AUTOSTART_FILE)
    1.94 +
    1.95 +$(PACKAGE): $(OBJS)
    1.96 +	$(LINK.o) $^ $(LDLIBS) -o $@
    1.97 +
    1.98 +$(POTFILE): po/POTFILES.in $(POSRCS)
    1.99 +	cd po/ && $(INTLTOOL_UPDATE) --pot --gettext-package="$(PACKAGE)"
   1.100 +
   1.101 +pot: $(POTFILE)
   1.102 +
   1.103 +update-po: $(POTFILE)
   1.104 +	cd po/ && for lang in $(patsubst po/%.mo,%,$(MOFILES)); do \
   1.105 +	    $(INTLTOOL_UPDATE) --dist --gettext-package="$(PACKAGE)" \
   1.106 +	    $${lang}; \
   1.107 +	done
   1.108 +
   1.109 +%.o: %.c
   1.110 +	$(MAKEDEPEND.c) $< | $(SED) -f deps.sed >$*.d
   1.111 +	$(COMPILE.c) -o $@ $<
   1.112 +
   1.113 +%.desktop: %.desktop.in $(MOFILES)
   1.114 +	$(INTLTOOL_MERGE) --desktop-style --utf8 po $< $@
   1.115 +
   1.116 +%.mo: %.po
   1.117 +	$(MSGFMT) -o $@ $<
   1.118 +
   1.119 +install:
   1.120 +	$(INSTALL.exec) $(PACKAGE) "$(DESTDIR)$(bindir)/$(PACKAGE)"
   1.121 +	for lang in $(patsubst po/%.mo,%,$(MOFILES)); do \
   1.122 +	    $(INSTALL.data) po/$${lang}.mo \
   1.123 +	        "$(DESTDIR)$(localedir)/$${lang}/LC_MESSAGES/$(PACKAGE).mo"; \
   1.124 +	done
   1.125 +	$(INSTALL.data) $(AUTOSTART_FILE) \
   1.126 +	        "$(DESTDIR)$(xdgautostartdir)/$(notdir $(AUTOSTART_FILE))"
   1.127 +
   1.128 +clean:
   1.129 +	rm -f $(PACKAGE) $(OBJS) $(POTFILE) $(MOFILES) $(AUTOSTART_FILE)
   1.130 +
   1.131 +clobber: clean
   1.132 +	rm -f $(patsubst %.o,%.d,$(OBJS))
   1.133 +
   1.134 +dist: clobber
   1.135 +	$(PAX) -w -x ustar -s ',.*/\..*,,' -s ',./[^/]*\.tar\.gz,,' \
   1.136 +	    -s ',\./,$(DISTNAME)/,' . | $(GZIP) > $(DISTNAME).tar.gz
   1.137 +
   1.138 +-include $(patsubst %.o,%.d,$(OBJS))
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/deps.sed	Sun Apr 27 23:07:51 2014 +0200
     2.3 @@ -0,0 +1,26 @@
     2.4 +/^[^:]\{1,\}:.*\\$/{
     2.5 +    h
     2.6 +    s/\([^:]\{1,\}:\).*/\1/
     2.7 +    x
     2.8 +    s/[^:]\{1,\}://
     2.9 +}
    2.10 +/\\$/,/^$/bgen
    2.11 +/\\$/,/[^\\]$/{
    2.12 +:gen
    2.13 +    s/[[:blank:]]*\\$//
    2.14 +    s/^[[:blank:]]*//
    2.15 +    G
    2.16 +    s/\(.*\)\n\(.*\)/\2 \1/
    2.17 +}
    2.18 +/^[^:]\{1,\}:[[:blank:]]*$/d
    2.19 +/^[^:]\{1,\}\.o:/{
    2.20 +    s/[[:blank:]]*[^[:blank:]]\{1,\}\.[cC][[:blank:]]*/ /g
    2.21 +    s/[[:blank:]]*[^[:blank:]]\{1,\}\.[cC]$//g
    2.22 +    s/[[:blank:]]*[^[:blank:]]\{1,\}\.cc[[:blank:]]*/ /g
    2.23 +    s/[[:blank:]]*[^[:blank:]]\{1,\}\.cc$//g
    2.24 +    s/[[:blank:]]*[^[:blank:]]\{1,\}\.cpp[[:blank:]]*/ /g
    2.25 +    s/[[:blank:]]*[^[:blank:]]\{1,\}\.cpp$//g
    2.26 +    /^[^:]\{1,\}:[[:blank:]]*$/d
    2.27 +    s/^\([^:]\{1,\}\)\.o[[:blank:]]*:[[:blank:]]*\(.*\)/\1.d: $(wildcard \2)\
    2.28 +&/
    2.29 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/main.c	Sun Apr 27 23:07:51 2014 +0200
     3.3 @@ -0,0 +1,479 @@
     3.4 +/*
     3.5 + * Copyright (C) 2011 Guido Berhoerster <guido+xwrited@berhoerster.name>
     3.6 + *
     3.7 + * Permission is hereby granted, free of charge, to any person obtaining
     3.8 + * a copy of this software and associated documentation files (the
     3.9 + * "Software"), to deal in the Software without restriction, including
    3.10 + * without limitation the rights to use, copy, modify, merge, publish,
    3.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    3.12 + * permit persons to whom the Software is furnished to do so, subject to
    3.13 + * the following conditions:
    3.14 + *
    3.15 + * The above copyright notice and this permission notice shall be included
    3.16 + * in all copies or substantial portions of the Software.
    3.17 + *
    3.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    3.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    3.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    3.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    3.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    3.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    3.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    3.25 + */
    3.26 +
    3.27 +#define	_XOPEN_SOURCE	600
    3.28 +
    3.29 +#include <stdio.h>
    3.30 +#include <stdlib.h>
    3.31 +#include <string.h>
    3.32 +#include <unistd.h>
    3.33 +#include <fcntl.h>
    3.34 +#include <sys/stat.h>
    3.35 +#include <signal.h>
    3.36 +#include <errno.h>
    3.37 +#include <locale.h>
    3.38 +#include <libintl.h>
    3.39 +#include <glib.h>
    3.40 +#include <glib/gi18n.h>
    3.41 +#include <libnotify/notify.h>
    3.42 +#include "xwrited-debug.h"
    3.43 +#include "xwrited-unique.h"
    3.44 +#include "xwrited-utmp.h"
    3.45 +
    3.46 +enum {
    3.47 +	PIPE_R_FD = 0,
    3.48 +	PIPE_W_FD
    3.49 +};
    3.50 +
    3.51 +static int	signal_pipe_fd[2] = { -1, -1 };
    3.52 +
    3.53 +static void
    3.54 +on_signal(int signo)
    3.55 +{
    3.56 +	int		old_errno = errno;
    3.57 +	ssize_t		n;
    3.58 +	sigset_t	sigset;
    3.59 +
    3.60 +	/* try to read unread signals from the pipe and add the new one to it */
    3.61 +	n = read(signal_pipe_fd[PIPE_R_FD], &sigset, sizeof (sigset));
    3.62 +	if ((n == -1) || ((size_t)n < sizeof (sigset))) {
    3.63 +		sigemptyset(&sigset);
    3.64 +	}
    3.65 +	sigaddset(&sigset, signo);
    3.66 +	write(signal_pipe_fd[PIPE_W_FD], &sigset, sizeof (sigset));
    3.67 +
    3.68 +	errno = old_errno;
    3.69 +}
    3.70 +
    3.71 +static gboolean
    3.72 +signal_read_cb(GIOChannel *source, GIOCondition cond, gpointer user_data)
    3.73 +{
    3.74 +	GMainLoop	*loop = (GMainLoop *)user_data;
    3.75 +	sigset_t	sigset;
    3.76 +	sigset_t	old_sigset;
    3.77 +	GIOStatus	status;
    3.78 +	gsize		n;
    3.79 +	GError		*error = NULL;
    3.80 +
    3.81 +	/*
    3.82 +	 * deal with pending signals previously received in the signal handler,
    3.83 +	 * try to read a sigset from the pipe, avoid partial reads by blocking
    3.84 +	 * all signals during the read operation
    3.85 +	 */
    3.86 +	sigfillset(&sigset);
    3.87 +	sigprocmask(SIG_BLOCK, &sigset, &old_sigset);
    3.88 +	status = g_io_channel_read_chars(source, (gchar *)&sigset,
    3.89 +	    sizeof (sigset), &n, &error);
    3.90 +	sigprocmask(SIG_SETMASK, &old_sigset, NULL);
    3.91 +	if (status != G_IO_STATUS_NORMAL) {
    3.92 +		if (status != G_IO_STATUS_AGAIN) {
    3.93 +			if (error != NULL) {
    3.94 +				g_critical("failed to read from signal pipe: "
    3.95 +				    "%s", error->message);
    3.96 +				g_error_free(error);
    3.97 +				g_main_loop_quit(loop);
    3.98 +			} else {
    3.99 +				g_critical("failed to read from signal pipe");
   3.100 +				g_main_loop_quit(loop);
   3.101 +			}
   3.102 +		}
   3.103 +	} else if (n != sizeof (sigset)) {
   3.104 +		g_critical("short read from signal pipe");
   3.105 +		g_main_loop_quit(loop);
   3.106 +	} else {
   3.107 +		if ((sigismember(&sigset, SIGINT) == 1) ||
   3.108 +		    (sigismember(&sigset, SIGTERM) == 1) ||
   3.109 +		    (sigismember(&sigset, SIGQUIT) == 1) ||
   3.110 +		    (sigismember(&sigset, SIGHUP) == 1)) {
   3.111 +			g_debug("received signal, exiting");
   3.112 +			g_main_loop_quit(loop);
   3.113 +		}
   3.114 +	}
   3.115 +
   3.116 +	return (TRUE);
   3.117 +}
   3.118 +
   3.119 +static gboolean
   3.120 +send_notification(GString *raw_str, GMainLoop *loop)
   3.121 +{
   3.122 +	gboolean	retval = FALSE;
   3.123 +	GString		*utf8_str = NULL;
   3.124 +	gchar		*startp = raw_str->str;
   3.125 +	gchar		*endp;
   3.126 +	GRegex		*regex = NULL;
   3.127 +	GError		*error = NULL;
   3.128 +	gchar		*body = NULL;
   3.129 +	GList		*capabilities = NULL;
   3.130 +	gchar		*tmp;
   3.131 +	NotifyNotification *notification = NULL;
   3.132 +
   3.133 +	utf8_str = g_string_sized_new(raw_str->len);
   3.134 +	while (!g_utf8_validate(startp, raw_str->str + raw_str->len -
   3.135 +	    startp, (const gchar **)&endp)) {
   3.136 +		g_string_append_len(utf8_str, startp, endp - startp);
   3.137 +		/*
   3.138 +		 * replace each byte that does not belong to a UTF-8-encoded
   3.139 +		 * character with the Unicode REPLACEMENT CHARACTER (U+FFFD)
   3.140 +		 */
   3.141 +		g_string_append(utf8_str, "\357\277\275");
   3.142 +
   3.143 +		startp = endp + ((endp < raw_str->str + raw_str->len) ? 1 : 0);
   3.144 +	}
   3.145 +	g_string_append_len(utf8_str, startp, raw_str->str + raw_str->len -
   3.146 +	    startp);
   3.147 +
   3.148 +	/* remove any CR, BEL and trailing space and tabs */
   3.149 +	regex = g_regex_new("([\r\a]+|[ \t\r\a]+$)", G_REGEX_MULTILINE, 0,
   3.150 +	    &error);
   3.151 +	if (error != NULL) {
   3.152 +		goto out;
   3.153 +	}
   3.154 +	body = g_regex_replace_literal(regex, utf8_str->str, -1, 0, "", 0,
   3.155 +	    &error);
   3.156 +	if (error != NULL) {
   3.157 +		goto out;
   3.158 +	}
   3.159 +
   3.160 +	/*
   3.161 +	 * skip empty messages or messages only consisting of whitespace and
   3.162 +	 * control characters
   3.163 +	 */
   3.164 +	if ((strlen(body) == 0) ||
   3.165 +	    !g_regex_match_simple("[^[:space:][:cntrl:]]", body, 0, 0)) {
   3.166 +		goto out;
   3.167 +	}
   3.168 +
   3.169 +	/*
   3.170 +	 * if the notification daemon supports markup the message needs to be
   3.171 +	 * escaped
   3.172 +	 */
   3.173 +	capabilities = notify_get_server_caps();
   3.174 +	if (g_list_find_custom(capabilities, "body-markup",
   3.175 +	    (GCompareFunc)strcmp) != NULL) {
   3.176 +		tmp = g_markup_escape_text(body, -1);
   3.177 +		g_free(body);
   3.178 +		body = tmp;
   3.179 +	}
   3.180 +
   3.181 +	/* show notification */
   3.182 +	notification = notify_notification_new(_("Message received"),
   3.183 +	    body, "utilities-terminal"
   3.184 +#if !defined(NOTIFY_VERSION_MINOR) || \
   3.185 +    (NOTIFY_VERSION_MAJOR == 0 && NOTIFY_VERSION_MINOR < 7)
   3.186 +	    , NULL
   3.187 +#endif
   3.188 +	    );
   3.189 +	if (notification == NULL) {
   3.190 +		g_critical("failed to create a notification object");
   3.191 +		g_main_loop_quit(loop);
   3.192 +		goto out;
   3.193 +	}
   3.194 +	notify_notification_set_timeout(notification, NOTIFY_EXPIRES_NEVER);
   3.195 +	retval = notify_notification_show(notification, NULL);
   3.196 +
   3.197 +out:
   3.198 +	if (notification != NULL) {
   3.199 +		g_object_unref(G_OBJECT(notification));
   3.200 +	}
   3.201 +	if (capabilities != NULL) {
   3.202 +		g_list_free_full(capabilities, g_free);
   3.203 +	}
   3.204 +	g_free(body);
   3.205 +	if (regex != NULL) {
   3.206 +		g_regex_unref(regex);
   3.207 +	}
   3.208 +	if (utf8_str != NULL) {
   3.209 +		g_string_free(utf8_str, TRUE);
   3.210 +	}
   3.211 +
   3.212 +	return (retval);
   3.213 +}
   3.214 +
   3.215 +static gboolean
   3.216 +master_pty_read_cb(GIOChannel *source, GIOCondition cond,
   3.217 +    gpointer user_data)
   3.218 +{
   3.219 +	GMainLoop	*loop = (GMainLoop *)user_data;
   3.220 +	gchar		buf[BUFSIZ];
   3.221 +	GString		*raw_str = NULL;
   3.222 +	GIOStatus	status;
   3.223 +	gsize		buf_len;
   3.224 +	GError		*error = NULL;
   3.225 +
   3.226 +	if ((cond & G_IO_IN) || (cond & G_IO_PRI)) {
   3.227 +		raw_str = g_string_sized_new(BUFSIZ);
   3.228 +		/* read message from master pty */
   3.229 +		while ((status = g_io_channel_read_chars(source, buf, BUFSIZ,
   3.230 +		    &buf_len, &error)) == G_IO_STATUS_NORMAL) {
   3.231 +			if (buf_len > 0) {
   3.232 +				g_debug("read %" G_GSSIZE_FORMAT " bytes from "
   3.233 +				    "master pty", buf_len);
   3.234 +				g_string_append_len(raw_str, buf,
   3.235 +				    (gssize)buf_len);
   3.236 +			}
   3.237 +		}
   3.238 +		if (error != NULL) {
   3.239 +			g_critical("failed to read from master pty: %s",
   3.240 +			    error->message);
   3.241 +			g_error_free(error);
   3.242 +			g_main_loop_quit(loop);
   3.243 +			goto out;
   3.244 +		}
   3.245 +
   3.246 +		if (!send_notification(raw_str, loop)) {
   3.247 +			g_warning("failed to send notification");
   3.248 +		}
   3.249 +	}
   3.250 +
   3.251 +	if ((cond & G_IO_ERR) || (cond & G_IO_HUP)) {
   3.252 +		g_critical("connection to master pty broken");
   3.253 +		g_main_loop_quit(loop);
   3.254 +			goto out;
   3.255 +	}
   3.256 +
   3.257 +out:
   3.258 +	if (raw_str != NULL) {
   3.259 +		g_string_free(raw_str, TRUE);
   3.260 +	}
   3.261 +
   3.262 +	return (TRUE);
   3.263 +}
   3.264 +
   3.265 +int
   3.266 +main(int argc, char *argv[])
   3.267 +{
   3.268 +	int		status = EXIT_FAILURE;
   3.269 +	GMainLoop	*loop = NULL;
   3.270 +	GError		*error = NULL;
   3.271 +	XWritedUnique	*app = NULL;
   3.272 +	GOptionContext	*context = NULL;
   3.273 +	struct sigaction sigact;
   3.274 +	GIOChannel	*signal_channel = NULL;
   3.275 +	GIOChannel	*master_pty_channel = NULL;
   3.276 +	int		masterfd = -1;
   3.277 +	int		slavefd = -1;
   3.278 +	char		*slave_name = NULL;
   3.279 +	gboolean	vflag = FALSE;
   3.280 +	gboolean	dflag = FALSE;
   3.281 +	const GOptionEntry options[] = {
   3.282 +	    { "debug", 'd', 0, G_OPTION_ARG_NONE, &dflag,
   3.283 +	    N_("Show extra debugging information"), NULL },
   3.284 +	    { "version", 'V', 0, G_OPTION_ARG_NONE, &vflag,
   3.285 +	    N_("Print the current version and exit"), NULL },
   3.286 +	    { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, 0 }
   3.287 +	};
   3.288 +
   3.289 +	setlocale(LC_ALL, "");
   3.290 +	bindtextdomain(PACKAGE, LOCALEDIR);
   3.291 +	bind_textdomain_codeset(PACKAGE, "UTF-8");
   3.292 +	textdomain(PACKAGE);
   3.293 +
   3.294 +	g_type_init();
   3.295 +
   3.296 +	context = g_option_context_new("- display write and wall messages as "
   3.297 +	    "desktop notifications");
   3.298 +	g_option_context_add_main_entries(context, options, PACKAGE);
   3.299 +	g_option_context_set_translation_domain(context, PACKAGE);
   3.300 +	g_option_context_parse(context, &argc, &argv, &error);
   3.301 +	if (error != NULL) {
   3.302 +		g_printerr("%s.\n", error->message);
   3.303 +		g_error_free(error);
   3.304 +		goto out;
   3.305 +	}
   3.306 +
   3.307 +	xwrited_debug_init(dflag);
   3.308 +
   3.309 +	if (vflag) {
   3.310 +		g_print("%s %s\n", PACKAGE, VERSION);
   3.311 +		status = EXIT_SUCCESS;
   3.312 +		goto out;
   3.313 +	}
   3.314 +
   3.315 +	app = xwrited_unique_new("org.guido-berhoerster.code.xwrited");
   3.316 +	if (app == NULL) {
   3.317 +		g_critical("failed to initialize application");
   3.318 +		goto out;
   3.319 +	}
   3.320 +	if (!xwrited_unique_is_unique(app)) {
   3.321 +		g_printerr(_("xwrited is already running in this session.\n"));
   3.322 +		goto out;
   3.323 +	}
   3.324 +
   3.325 +	if (!notify_init(APP_NAME)) {
   3.326 +		g_critical("failed to initialize libnotify");
   3.327 +		goto out;
   3.328 +	}
   3.329 +
   3.330 +	loop = g_main_loop_new(NULL, FALSE);
   3.331 +	if (loop == NULL) {
   3.332 +		g_critical("failed to create main loop");
   3.333 +		goto out;
   3.334 +	}
   3.335 +
   3.336 +	/* open master pty */
   3.337 +	masterfd = posix_openpt(O_RDWR | O_NOCTTY);
   3.338 +	if (masterfd == -1) {
   3.339 +		g_critical("failed to open master pty: %s", g_strerror(errno));
   3.340 +		goto out;
   3.341 +	}
   3.342 +
   3.343 +	/* create slave pty */
   3.344 +	if ((grantpt(masterfd) == -1) || (unlockpt(masterfd) == -1)) {
   3.345 +		g_critical("failed to create slave pty: %s", g_strerror(errno));
   3.346 +		goto out;
   3.347 +	}
   3.348 +	slave_name = ptsname(masterfd);
   3.349 +	if (slave_name == NULL) {
   3.350 +		g_critical("failed to obtain name of slave pty");
   3.351 +		goto out;
   3.352 +	}
   3.353 +
   3.354 +	/*
   3.355 +	 * keep an open fd around order to prevent closing the master fd when
   3.356 +	 * receiving an EOF
   3.357 +	 */
   3.358 +	slavefd = open(slave_name, O_RDWR);
   3.359 +	if (slavefd == -1) {
   3.360 +		g_critical("failed to open slave pty: %s", g_strerror(errno));
   3.361 +		goto out;
   3.362 +	}
   3.363 +
   3.364 +	/* create a GIOChannel for monitoring the master pty for messages */
   3.365 +	master_pty_channel = g_io_channel_unix_new(masterfd);
   3.366 +	g_io_channel_set_flags(master_pty_channel,
   3.367 +	    g_io_channel_get_flags(master_pty_channel) | G_IO_FLAG_NONBLOCK,
   3.368 +	    &error);
   3.369 +	if (error != NULL) {
   3.370 +		g_critical("failed set flags on the master pty channel: %s",
   3.371 +		    error->message);
   3.372 +		g_error_free(error);
   3.373 +		goto out;
   3.374 +	}
   3.375 +	if (!g_io_add_watch(master_pty_channel, G_IO_IN | G_IO_PRI | G_IO_HUP |
   3.376 +	    G_IO_ERR, master_pty_read_cb, loop)) {
   3.377 +		g_critical("failed to add watch on signal channel");
   3.378 +		goto out;
   3.379 +	}
   3.380 +
   3.381 +	/* create pipe for delivering signals to a listener in the main loop */
   3.382 +	if (pipe(signal_pipe_fd) == -1) {
   3.383 +		g_critical("failed to create signal pipe: %s",
   3.384 +		    g_strerror(errno));
   3.385 +		goto out;
   3.386 +	}
   3.387 +	if (fcntl(signal_pipe_fd[PIPE_W_FD], F_SETFL, O_NONBLOCK) == -1) {
   3.388 +		g_critical("failed to set flags on signal pipe: %s",
   3.389 +		    g_strerror(errno));
   3.390 +		goto out;
   3.391 +	}
   3.392 +
   3.393 +	/* create GIO channel for reading from the signal_pipe */
   3.394 +	signal_channel = g_io_channel_unix_new(signal_pipe_fd[PIPE_R_FD]);
   3.395 +	g_io_channel_set_encoding(signal_channel, NULL, &error);
   3.396 +	if (error != NULL) {
   3.397 +		g_critical("failed to set binary encoding for signal channel: "
   3.398 +		    "%s", error->message);
   3.399 +		g_error_free(error);
   3.400 +		goto out;
   3.401 +	}
   3.402 +	g_io_channel_set_buffered(signal_channel, FALSE);
   3.403 +	g_io_channel_set_flags(signal_channel,
   3.404 +	    g_io_channel_get_flags(signal_channel) | G_IO_FLAG_NONBLOCK,
   3.405 +	    &error);
   3.406 +	if (error != NULL) {
   3.407 +		g_critical("failed set flags on signal channel: %s",
   3.408 +		    error->message);
   3.409 +		g_error_free(error);
   3.410 +		goto out;
   3.411 +	}
   3.412 +	if (g_io_add_watch(signal_channel, G_IO_IN | G_IO_PRI | G_IO_HUP |
   3.413 +	    G_IO_ERR, signal_read_cb, loop) == 0) {
   3.414 +		g_critical("failed to add watch on the signal channel");
   3.415 +		goto out;
   3.416 +	}
   3.417 +
   3.418 +	/* set up signal handler */
   3.419 +	sigact.sa_handler = on_signal;
   3.420 +	sigact.sa_flags = SA_RESTART;
   3.421 +	sigemptyset(&sigact.sa_mask);
   3.422 +	if ((sigaction(SIGINT, &sigact, NULL) < 0) ||
   3.423 +	    (sigaction(SIGTERM, &sigact, NULL) < 0) ||
   3.424 +	    (sigaction(SIGQUIT, &sigact, NULL) < 0) ||
   3.425 +	    (sigaction(SIGHUP, &sigact, NULL) < 0)) {
   3.426 +		g_critical("failed to set up signal handler");
   3.427 +		goto out;
   3.428 +	}
   3.429 +
   3.430 +	xwrited_utmp_add_entry(masterfd);
   3.431 +
   3.432 +	/* main loop */
   3.433 +	g_main_loop_run(loop);
   3.434 +
   3.435 +	xwrited_utmp_remove_entry(masterfd);
   3.436 +
   3.437 +	status = EXIT_SUCCESS;
   3.438 +
   3.439 +out:
   3.440 +	if (context != NULL) {
   3.441 +		g_option_context_free(context);
   3.442 +	}
   3.443 +
   3.444 +	if (signal_channel != NULL) {
   3.445 +		g_io_channel_shutdown(signal_channel, FALSE, NULL);
   3.446 +		g_io_channel_unref(signal_channel);
   3.447 +	}
   3.448 +
   3.449 +	if (signal_pipe_fd[PIPE_R_FD] != -1) {
   3.450 +		close(signal_pipe_fd[PIPE_R_FD]);
   3.451 +	}
   3.452 +	if (signal_pipe_fd[PIPE_W_FD] != -1) {
   3.453 +		close(signal_pipe_fd[PIPE_W_FD]);
   3.454 +	}
   3.455 +
   3.456 +	if (master_pty_channel != NULL) {
   3.457 +		g_io_channel_shutdown(master_pty_channel, FALSE, NULL);
   3.458 +		g_io_channel_unref(master_pty_channel);
   3.459 +	}
   3.460 +
   3.461 +	if (slavefd != -1) {
   3.462 +		close(slavefd);
   3.463 +	}
   3.464 +
   3.465 +	if (masterfd != -1) {
   3.466 +		close(masterfd);
   3.467 +	}
   3.468 +
   3.469 +	if (app != NULL) {
   3.470 +		g_object_unref(app);
   3.471 +	}
   3.472 +
   3.473 +	if (loop != NULL) {
   3.474 +		g_main_loop_unref(loop);
   3.475 +	}
   3.476 +
   3.477 +	if (notify_is_initted()) {
   3.478 +		notify_uninit();
   3.479 +	}
   3.480 +
   3.481 +	exit(status);
   3.482 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/po/POTFILES.in	Sun Apr 27 23:07:51 2014 +0200
     4.3 @@ -0,0 +1,6 @@
     4.4 +xwrited.desktop.in
     4.5 +main.c
     4.6 +xwrited-debug.c
     4.7 +xwrited-unique.c
     4.8 +xwrited-utmp-utempter.c
     4.9 +xwrited-utmp-utmpx.c
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/po/de.po	Sun Apr 27 23:07:51 2014 +0200
     5.3 @@ -0,0 +1,45 @@
     5.4 +# German translations for xwrited package
     5.5 +# German messages for xwrited.
     5.6 +# Copyright (C) 2011 Guido Berhoerster.
     5.7 +# This file is distributed under the same license as the xwrited package.
     5.8 +# Guido Berhoerster <guido+xwrited@berhoerster.name>, 2011.
     5.9 +#
    5.10 +msgid ""
    5.11 +msgstr ""
    5.12 +"Project-Id-Version: xwrited 1\n"
    5.13 +"Report-Msgid-Bugs-To: \n"
    5.14 +"POT-Creation-Date: 2011-05-20 16:48+0200\n"
    5.15 +"PO-Revision-Date: 2010-07-28 13:00+0200\n"
    5.16 +"Last-Translator: Guido Berhoerster <guido+xwrited@berhoerster.name>\n"
    5.17 +"Language-Team: German\n"
    5.18 +"Language: \n"
    5.19 +"MIME-Version: 1.0\n"
    5.20 +"Content-Type: text/plain; charset=UTF-8\n"
    5.21 +"Content-Transfer-Encoding: 8bit\n"
    5.22 +"Plural-Forms: nplurals=2; plural=(n != 1);\n"
    5.23 +
    5.24 +#: ../xwrited.desktop.in.h:1
    5.25 +msgid "xwrited"
    5.26 +msgstr "xwrited"
    5.27 +
    5.28 +#: ../xwrited.desktop.in.h:2
    5.29 +msgid "Display write and wall messages as desktop notifications"
    5.30 +msgstr "Zeigt write und wall Nachrichten as Desktop-Benachrichtigungen an"
    5.31 +
    5.32 +#. show notification
    5.33 +#: ../main.c:179
    5.34 +msgid "Message received"
    5.35 +msgstr "Nachricht erhalten"
    5.36 +
    5.37 +#: ../main.c:280
    5.38 +msgid "Show extra debugging information"
    5.39 +msgstr "Zusätzliche Debugging-Informationen anzeigen"
    5.40 +
    5.41 +#: ../main.c:282
    5.42 +msgid "Print the current version and exit"
    5.43 +msgstr "Aktuelle Version zeigen und beenden"
    5.44 +
    5.45 +#: ../main.c:321
    5.46 +#, c-format
    5.47 +msgid "xwrited is already running in this session.\n"
    5.48 +msgstr "xwrited läuft bereits in dieser Sitzung.\n"
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/xwrited-debug.c	Sun Apr 27 23:07:51 2014 +0200
     6.3 @@ -0,0 +1,46 @@
     6.4 +/*
     6.5 + * Copyright (C) 2011 Guido Berhoerster <guido+xwrited@berhoerster.name>
     6.6 + *
     6.7 + * Permission is hereby granted, free of charge, to any person obtaining
     6.8 + * a copy of this software and associated documentation files (the
     6.9 + * "Software"), to deal in the Software without restriction, including
    6.10 + * without limitation the rights to use, copy, modify, merge, publish,
    6.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    6.12 + * permit persons to whom the Software is furnished to do so, subject to
    6.13 + * the following conditions:
    6.14 + *
    6.15 + * The above copyright notice and this permission notice shall be included
    6.16 + * in all copies or substantial portions of the Software.
    6.17 + *
    6.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    6.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    6.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    6.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    6.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    6.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    6.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    6.25 + */
    6.26 +
    6.27 +#define	_XOPEN_SOURCE	600
    6.28 +
    6.29 +#include <string.h>
    6.30 +#include <stdarg.h>
    6.31 +#include <glib.h>
    6.32 +
    6.33 +#include "xwrited-debug.h"
    6.34 +
    6.35 +static void
    6.36 +dummy_log_handler(const gchar *log_domain, GLogLevelFlags log_level,
    6.37 +    const gchar *message, gpointer data)
    6.38 +{
    6.39 +	/* drop all messages */
    6.40 +}
    6.41 +
    6.42 +void
    6.43 +xwrited_debug_init(gboolean debug_mode)
    6.44 +{
    6.45 +	if (!debug_mode) {
    6.46 +		g_log_set_handler(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
    6.47 +		    dummy_log_handler, NULL);
    6.48 +	}
    6.49 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/xwrited-debug.h	Sun Apr 27 23:07:51 2014 +0200
     7.3 @@ -0,0 +1,35 @@
     7.4 +/*
     7.5 + * Copyright (C) 2011 Guido Berhoerster <guido+xwrited@berhoerster.name>
     7.6 + *
     7.7 + * Permission is hereby granted, free of charge, to any person obtaining
     7.8 + * a copy of this software and associated documentation files (the
     7.9 + * "Software"), to deal in the Software without restriction, including
    7.10 + * without limitation the rights to use, copy, modify, merge, publish,
    7.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    7.12 + * permit persons to whom the Software is furnished to do so, subject to
    7.13 + * the following conditions:
    7.14 + *
    7.15 + * The above copyright notice and this permission notice shall be included
    7.16 + * in all copies or substantial portions of the Software.
    7.17 + *
    7.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    7.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    7.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    7.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    7.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    7.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    7.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    7.25 + */
    7.26 +
    7.27 +#ifndef	XWRITED_DEBUG_H
    7.28 +#define	XWRITED_DEBUG_H
    7.29 +
    7.30 +#include <glib.h>
    7.31 +
    7.32 +G_BEGIN_DECLS
    7.33 +
    7.34 +void	xwrited_debug_init(gboolean);
    7.35 +
    7.36 +G_END_DECLS
    7.37 +
    7.38 +#endif /* XWRITED_DEBUG_H */
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/xwrited-unique.c	Sun Apr 27 23:07:51 2014 +0200
     8.3 @@ -0,0 +1,236 @@
     8.4 +/*
     8.5 + * Copyright (C) 2011 Guido Berhoerster <guido+xwrited@berhoerster.name>
     8.6 + *
     8.7 + * Permission is hereby granted, free of charge, to any person obtaining
     8.8 + * a copy of this software and associated documentation files (the
     8.9 + * "Software"), to deal in the Software without restriction, including
    8.10 + * without limitation the rights to use, copy, modify, merge, publish,
    8.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    8.12 + * permit persons to whom the Software is furnished to do so, subject to
    8.13 + * the following conditions:
    8.14 + *
    8.15 + * The above copyright notice and this permission notice shall be included
    8.16 + * in all copies or substantial portions of the Software.
    8.17 + *
    8.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    8.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    8.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    8.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    8.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    8.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    8.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    8.25 + */
    8.26 +
    8.27 +#define	_XOPEN_SOURCE	600
    8.28 +
    8.29 +#include <glib.h>
    8.30 +#include <dbus/dbus-glib.h>
    8.31 +#include <dbus/dbus.h>
    8.32 +
    8.33 +#include "xwrited-unique.h"
    8.34 +
    8.35 +G_DEFINE_TYPE(XWritedUnique, xwrited_unique, G_TYPE_OBJECT)
    8.36 +
    8.37 +#define	XWRITED_UNIQUE_GET_PRIVATE(obj)	(G_TYPE_INSTANCE_GET_PRIVATE((obj), \
    8.38 +    XWRITED_TYPE_UNIQUE, XWritedUniquePrivate))
    8.39 +
    8.40 +struct	_XWritedUniquePrivate {
    8.41 +	DBusGConnection	*session_bus;
    8.42 +	DBusGProxy	*session_bus_proxy;
    8.43 +	gchar		*name;
    8.44 +	gboolean	is_unique;
    8.45 +};
    8.46 +
    8.47 +enum {
    8.48 +	PROP_0,
    8.49 +	PROP_NAME,
    8.50 +	PROP_IS_XWRITED_UNIQUE
    8.51 +};
    8.52 +
    8.53 +static gboolean
    8.54 +request_name(XWritedUnique *self)
    8.55 +{
    8.56 +	guint32	request_name_response;
    8.57 +	GError	*error = NULL;
    8.58 +
    8.59 +	g_return_val_if_fail(self->priv->session_bus != NULL, FALSE);
    8.60 +	g_return_val_if_fail(self->priv->session_bus_proxy != NULL, FALSE);
    8.61 +
    8.62 +	if (dbus_g_proxy_call(self->priv->session_bus_proxy, "RequestName",
    8.63 +	    &error, G_TYPE_STRING, self->priv->name, G_TYPE_UINT,
    8.64 +	    DBUS_NAME_FLAG_DO_NOT_QUEUE, G_TYPE_INVALID, G_TYPE_UINT,
    8.65 +	    &request_name_response, G_TYPE_INVALID) == 0) {
    8.66 +		g_warning("failed to acquire service name \"%s\": %s",
    8.67 +		    self->priv->name, error->message);
    8.68 +		g_error_free(error);
    8.69 +		return (FALSE);
    8.70 +	}
    8.71 +
    8.72 +	switch (request_name_response) {
    8.73 +	case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
    8.74 +		return (TRUE);
    8.75 +	case DBUS_REQUEST_NAME_REPLY_EXISTS:
    8.76 +		break;
    8.77 +	default:
    8.78 +		g_warning("failed to acquire service name \"%s\"",
    8.79 +		    self->priv->name);
    8.80 +	}
    8.81 +
    8.82 +	return (FALSE);
    8.83 +}
    8.84 +
    8.85 +static void
    8.86 +xwrited_unique_get_property(GObject *gobject, guint property_id, GValue *value,
    8.87 +    GParamSpec *pspec)
    8.88 +{
    8.89 +	XWritedUnique	*app = XWRITED_UNIQUE(gobject);
    8.90 +
    8.91 +	switch (property_id) {
    8.92 +	case PROP_NAME:
    8.93 +		g_value_set_string(value, app->priv->name);
    8.94 +		break;
    8.95 +	case PROP_IS_XWRITED_UNIQUE:
    8.96 +		g_value_set_boolean(value, app->priv->is_unique);
    8.97 +		break;
    8.98 +	default:
    8.99 +		G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id,
   8.100 +		    pspec);
   8.101 +	}
   8.102 +}
   8.103 +
   8.104 +static void
   8.105 +xwrited_unique_set_property(GObject *gobject, guint property_id,
   8.106 +    const GValue *value, GParamSpec *pspec)
   8.107 +{
   8.108 +	XWritedUnique	*app = XWRITED_UNIQUE(gobject);
   8.109 +
   8.110 +	switch (property_id) {
   8.111 +	case PROP_NAME:
   8.112 +		app->priv->name = g_strdup(g_value_get_string(value));
   8.113 +		break;
   8.114 +	default:
   8.115 +		G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id,
   8.116 +		    pspec);
   8.117 +	}
   8.118 +}
   8.119 +
   8.120 +static GObject *
   8.121 +xwrited_unique_constructor(GType gtype, guint n_params,
   8.122 +    GObjectConstructParam *params)
   8.123 +{
   8.124 +	GObjectClass	*parent_class;
   8.125 +	GObject		*gobject;
   8.126 +	XWritedUnique	*app;
   8.127 +	GError		*error = NULL;
   8.128 +
   8.129 +	parent_class = G_OBJECT_CLASS(xwrited_unique_parent_class);
   8.130 +	gobject = parent_class->constructor(gtype, n_params, params);
   8.131 +	app = XWRITED_UNIQUE(gobject);
   8.132 +
   8.133 +	app->priv->session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
   8.134 +	if (app->priv->session_bus == NULL) {
   8.135 +		g_warning("failed to connect to DBus session bus: %s",
   8.136 +		    error->message);
   8.137 +		g_error_free(error);
   8.138 +		goto out;
   8.139 +	}
   8.140 +
   8.141 +	app->priv->session_bus_proxy =
   8.142 +	    dbus_g_proxy_new_for_name(app->priv->session_bus,
   8.143 +	    DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
   8.144 +	if (app->priv->session_bus_proxy == NULL) {
   8.145 +		g_warning("failed to create DBus proxy");
   8.146 +		goto out;
   8.147 +	}
   8.148 +
   8.149 +	if (request_name(app)) {
   8.150 +		app->priv->is_unique = TRUE;
   8.151 +	}
   8.152 +
   8.153 +out:
   8.154 +	return (gobject);
   8.155 +}
   8.156 +
   8.157 +static void
   8.158 +xwrited_unique_dispose(GObject *gobject)
   8.159 +{
   8.160 +	XWritedUnique	*self = XWRITED_UNIQUE(gobject);
   8.161 +
   8.162 +	if (self->priv->session_bus_proxy != NULL) {
   8.163 +		g_object_unref(self->priv->session_bus_proxy);
   8.164 +		self->priv->session_bus_proxy = NULL;
   8.165 +	}
   8.166 +
   8.167 +	G_OBJECT_CLASS(xwrited_unique_parent_class)->dispose(gobject);
   8.168 +}
   8.169 +
   8.170 +static void
   8.171 +xwrited_unique_finalize(GObject *gobject)
   8.172 +{
   8.173 +	XWritedUnique	*self = XWRITED_UNIQUE(gobject);
   8.174 +
   8.175 +	g_free(self->priv->name);
   8.176 +
   8.177 +	G_OBJECT_CLASS(xwrited_unique_parent_class)->finalize(gobject);
   8.178 +}
   8.179 +
   8.180 +static void
   8.181 +xwrited_unique_class_init(XWritedUniqueClass *klass)
   8.182 +{
   8.183 +	GObjectClass	*gobject_class = G_OBJECT_CLASS(klass);
   8.184 +	GParamSpec	*pspec;
   8.185 +
   8.186 +	gobject_class->constructor = xwrited_unique_constructor;
   8.187 +	gobject_class->get_property = xwrited_unique_get_property;
   8.188 +	gobject_class->set_property = xwrited_unique_set_property;
   8.189 +	gobject_class->dispose = xwrited_unique_dispose;
   8.190 +	gobject_class->finalize = xwrited_unique_finalize;
   8.191 +
   8.192 +	pspec = g_param_spec_string("name", "Name",
   8.193 +	    "The unique name of the application", NULL, G_PARAM_READABLE |
   8.194 +	    G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME |
   8.195 +	    G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
   8.196 +	g_object_class_install_property(gobject_class, PROP_NAME, pspec);
   8.197 +
   8.198 +	pspec = g_param_spec_boolean("is-unique", "Is unique",
   8.199 +	    "Whether the current application instance is unique", FALSE,
   8.200 +	    G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
   8.201 +	    G_PARAM_STATIC_BLURB);
   8.202 +	g_object_class_install_property(gobject_class, PROP_IS_XWRITED_UNIQUE,
   8.203 +	    pspec);
   8.204 +
   8.205 +	g_type_class_add_private(klass, sizeof (XWritedUniquePrivate));
   8.206 +}
   8.207 +
   8.208 +static void
   8.209 +xwrited_unique_init(XWritedUnique *self)
   8.210 +{
   8.211 +	self->priv = XWRITED_UNIQUE_GET_PRIVATE(self);
   8.212 +
   8.213 +	self->priv->is_unique = FALSE;
   8.214 +	self->priv->session_bus_proxy = NULL;
   8.215 +}
   8.216 +
   8.217 +XWritedUnique *
   8.218 +xwrited_unique_new(const gchar *name)
   8.219 +{
   8.220 +	XWritedUnique	*app;
   8.221 +
   8.222 +	g_return_val_if_fail(name != NULL, NULL);
   8.223 +
   8.224 +	app = g_object_new(XWRITED_TYPE_UNIQUE, "name", name, NULL);
   8.225 +	if (app->priv->session_bus_proxy == NULL) {
   8.226 +		g_object_unref(app);
   8.227 +		return (NULL);
   8.228 +	}
   8.229 +
   8.230 +	return (app);
   8.231 +}
   8.232 +
   8.233 +gboolean
   8.234 +xwrited_unique_is_unique(XWritedUnique *self)
   8.235 +{
   8.236 +	g_return_val_if_fail(XWRITED_IS_UNIQUE(self), FALSE);
   8.237 +
   8.238 +	return (self->priv->is_unique);
   8.239 +}
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/xwrited-unique.h	Sun Apr 27 23:07:51 2014 +0200
     9.3 @@ -0,0 +1,62 @@
     9.4 +/*
     9.5 + * Copyright (C) 2011 Guido Berhoerster <guido+xwrited@berhoerster.name>
     9.6 + *
     9.7 + * Permission is hereby granted, free of charge, to any person obtaining
     9.8 + * a copy of this software and associated documentation files (the
     9.9 + * "Software"), to deal in the Software without restriction, including
    9.10 + * without limitation the rights to use, copy, modify, merge, publish,
    9.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    9.12 + * permit persons to whom the Software is furnished to do so, subject to
    9.13 + * the following conditions:
    9.14 + *
    9.15 + * The above copyright notice and this permission notice shall be included
    9.16 + * in all copies or substantial portions of the Software.
    9.17 + *
    9.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    9.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    9.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    9.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    9.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    9.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    9.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    9.25 + */
    9.26 +
    9.27 +#ifndef	XWRITED_UNIQUE_H
    9.28 +#define	XWRITED_UNIQUE_H
    9.29 +
    9.30 +#include <glib.h>
    9.31 +#include <glib-object.h>
    9.32 +
    9.33 +G_BEGIN_DECLS
    9.34 +
    9.35 +#define	XWRITED_TYPE_UNIQUE		(xwrited_unique_get_type())
    9.36 +#define	XWRITED_UNIQUE(obj)		(G_TYPE_CHECK_INSTANCE_CAST((obj), \
    9.37 +    XWRITED_TYPE_UNIQUE, XWritedUnique))
    9.38 +#define	XWRITED_IS_UNIQUE(obj)		(G_TYPE_CHECK_INSTANCE_TYPE((obj), \
    9.39 +    XWRITED_TYPE_UNIQUE))
    9.40 +#define	XWRITED_UNIQUE_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST((klass), \
    9.41 +    XWRITED_TYPE_UNIQUE, XWritedUniqueClass))
    9.42 +#define	XWRITED_IS_UNIQUE_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), \
    9.43 +    XWRITED_TYPE_UNIQUE))
    9.44 +#define	XWRITED_UNIQUE_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), \
    9.45 +    XWRITED_TYPE_UNIQUE, XWritedUniqueClass))
    9.46 +
    9.47 +typedef struct _XWritedUnique		XWritedUnique;
    9.48 +typedef struct _XWritedUniqueClass	XWritedUniqueClass;
    9.49 +typedef struct _XWritedUniquePrivate	XWritedUniquePrivate;
    9.50 +
    9.51 +struct _XWritedUnique {
    9.52 +	GObject		parent_instance;
    9.53 +	XWritedUniquePrivate *priv;
    9.54 +};
    9.55 +
    9.56 +struct _XWritedUniqueClass {
    9.57 +	GObjectClass	parent_class;
    9.58 +};
    9.59 +
    9.60 +gboolean	xwrited_unique_is_unique(XWritedUnique *);
    9.61 +XWritedUnique *	xwrited_unique_new(const gchar *);
    9.62 +
    9.63 +G_END_DECLS
    9.64 +
    9.65 +#endif /* XWRITED_UNIQUE_H */
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/xwrited-utmp-utempter.c	Sun Apr 27 23:07:51 2014 +0200
    10.3 @@ -0,0 +1,45 @@
    10.4 +/*
    10.5 + * Copyright (C) 2010 Guido Berhoerster <guido+xwrited@berhoerster.name>
    10.6 + *
    10.7 + * Permission is hereby granted, free of charge, to any person obtaining
    10.8 + * a copy of this software and associated documentation files (the
    10.9 + * "Software"), to deal in the Software without restriction, including
   10.10 + * without limitation the rights to use, copy, modify, merge, publish,
   10.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   10.12 + * permit persons to whom the Software is furnished to do so, subject to
   10.13 + * the following conditions:
   10.14 + *
   10.15 + * The above copyright notice and this permission notice shall be included
   10.16 + * in all copies or substantial portions of the Software.
   10.17 + *
   10.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   10.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   10.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   10.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   10.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   10.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   10.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   10.25 + */
   10.26 +
   10.27 +#define	_XOPEN_SOURCE	600
   10.28 +
   10.29 +#include <stdlib.h>
   10.30 +#include <utempter.h>
   10.31 +
   10.32 +void
   10.33 +xwrited_utmp_add_entry(int fd)
   10.34 +{
   10.35 +	char	*pty_name;
   10.36 +
   10.37 +	pty_name = ptsname(fd);
   10.38 +	addToUtmp(pty_name, NULL, fd);
   10.39 +}
   10.40 +
   10.41 +void
   10.42 +xwrited_utmp_remove_entry(int fd)
   10.43 +{
   10.44 +	char	*pty_name;
   10.45 +
   10.46 +	pty_name = ptsname(fd);
   10.47 +	removeLineFromUtmp(pty_name, fd);
   10.48 +}
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/xwrited-utmp-utmpx.c	Sun Apr 27 23:07:51 2014 +0200
    11.3 @@ -0,0 +1,93 @@
    11.4 +/*
    11.5 + * Copyright (C) 2010 Guido Berhoerster <guido+xwrited@berhoerster.name>
    11.6 + *
    11.7 + * Permission is hereby granted, free of charge, to any person obtaining
    11.8 + * a copy of this software and associated documentation files (the
    11.9 + * "Software"), to deal in the Software without restriction, including
   11.10 + * without limitation the rights to use, copy, modify, merge, publish,
   11.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   11.12 + * permit persons to whom the Software is furnished to do so, subject to
   11.13 + * the following conditions:
   11.14 + *
   11.15 + * The above copyright notice and this permission notice shall be included
   11.16 + * in all copies or substantial portions of the Software.
   11.17 + *
   11.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   11.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   11.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   11.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   11.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   11.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   11.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   11.25 + */
   11.26 +
   11.27 +#define	_XOPEN_SOURCE	600
   11.28 +
   11.29 +#include <glib.h>
   11.30 +#include <stdlib.h>
   11.31 +#include <string.h>
   11.32 +#include <unistd.h>
   11.33 +#include <pwd.h>
   11.34 +#include <utmpx.h>
   11.35 +#include <errno.h>
   11.36 +#include <sys/time.h>
   11.37 +
   11.38 +#define	DEV_PREFIX	"/dev/"
   11.39 +
   11.40 +static void
   11.41 +utmp_write_entry(int fd, gboolean add)
   11.42 +{
   11.43 +	struct utmpx	utmpx;
   11.44 +	char		*line = NULL;
   11.45 +	size_t		line_len;
   11.46 +	char		*id;
   11.47 +	struct passwd	*pwd;
   11.48 +
   11.49 +	line = ptsname(fd);
   11.50 +	if (line == NULL) {
   11.51 +		g_critical("failed to obtain slave pty name");
   11.52 +		return;
   11.53 +	}
   11.54 +	if (g_str_has_prefix(line, DEV_PREFIX)) {
   11.55 +		line += strlen(DEV_PREFIX);
   11.56 +	}
   11.57 +
   11.58 +	line_len = strlen(line);
   11.59 +	id = (line_len >= sizeof (utmpx.ut_pid)) ?
   11.60 +	    line + (line_len - sizeof (utmpx.ut_pid)) :
   11.61 +	    line;
   11.62 +
   11.63 +	pwd = getpwuid(getuid());
   11.64 +	if (pwd == NULL) {
   11.65 +		g_critical("failed to get username: %s", g_strerror(errno));
   11.66 +		return;
   11.67 +	}
   11.68 +
   11.69 +	memset(&utmpx, 0, sizeof (utmpx));
   11.70 +	strncpy(utmpx.ut_name, pwd->pw_name, sizeof (utmpx.ut_name));
   11.71 +	strncpy(utmpx.ut_id, id, sizeof (utmpx.ut_id));
   11.72 +	strncpy(utmpx.ut_line, line, sizeof (utmpx.ut_line));
   11.73 +	utmpx.ut_pid = getpid();
   11.74 +	utmpx.ut_type = add ? USER_PROCESS : DEAD_PROCESS;
   11.75 +	gettimeofday(&utmpx.ut_tv, NULL);
   11.76 +
   11.77 +	setutxent();
   11.78 +	if (pututxline(&utmpx) == NULL) {
   11.79 +		g_critical("failed to write to utmpx database: %s",
   11.80 +		    g_strerror(errno));
   11.81 +		return;
   11.82 +	}
   11.83 +	endutxent();
   11.84 +}
   11.85 +
   11.86 +void
   11.87 +xwrited_utmp_add_entry(int fd)
   11.88 +{
   11.89 +	utmp_write_entry(fd, TRUE);
   11.90 +}
   11.91 +
   11.92 +void
   11.93 +xwrited_utmp_remove_entry(int fd)
   11.94 +{
   11.95 +	utmp_write_entry(fd, FALSE);
   11.96 +}
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/xwrited-utmp.h	Sun Apr 27 23:07:51 2014 +0200
    12.3 @@ -0,0 +1,36 @@
    12.4 +/*
    12.5 + * Copyright (C) 2010 Guido Berhoerster <guido+xwrited@berhoerster.name>
    12.6 + *
    12.7 + * Permission is hereby granted, free of charge, to any person obtaining
    12.8 + * a copy of this software and associated documentation files (the
    12.9 + * "Software"), to deal in the Software without restriction, including
   12.10 + * without limitation the rights to use, copy, modify, merge, publish,
   12.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   12.12 + * permit persons to whom the Software is furnished to do so, subject to
   12.13 + * the following conditions:
   12.14 + *
   12.15 + * The above copyright notice and this permission notice shall be included
   12.16 + * in all copies or substantial portions of the Software.
   12.17 + *
   12.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   12.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   12.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   12.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   12.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   12.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   12.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   12.25 + */
   12.26 +
   12.27 +#ifndef	XWRITED_UTMP_H
   12.28 +#define	XWRITED_UTMP_H
   12.29 +
   12.30 +#include <glib.h>
   12.31 +
   12.32 +G_BEGIN_DECLS
   12.33 +
   12.34 +void	xwrited_utmp_add_entry(int);
   12.35 +void	xwrited_utmp_remove_entry(int);
   12.36 +
   12.37 +G_END_DECLS
   12.38 +
   12.39 +#endif /* XWRITED_UTMP_H */
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/xwrited.desktop.in	Sun Apr 27 23:07:51 2014 +0200
    13.3 @@ -0,0 +1,8 @@
    13.4 +[Desktop Entry]
    13.5 +Encoding=UTF-8
    13.6 +_Name=xwrited
    13.7 +_Comment=Display write and wall messages as desktop notifications
    13.8 +Exec=xwrited
    13.9 +Terminal=false
   13.10 +Type=Application
   13.11 +Categories=System;Monitor;