Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 218 lines (177 sloc) 5.102 kB
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
1 /* System-wide notify-send wrapper
2 * (c) 2010 Michał Górny
3 * Released under the terms of 3-clause BSD license
4 */
5
feb972c @mgorny Use config.h file.
authored
6 #ifdef HAVE_CONFIG_H
7 # include "config.h"
8 #endif
9
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <libgen.h>
14
15 #include <sys/types.h>
16 #include <sys/wait.h>
17 #include <pwd.h>
18 #include <unistd.h>
19
20 #include <proc/readproc.h>
21
79978cc @mgorny Migrate to using libtinynotify.
authored
22 #include <tinynotify.h>
23 #include <tinynotify-cli.h>
24
335e2c7 @mgorny Use <sysexits.h> whenever available.
authored
25 #ifdef HAVE_SYSEXITS_H
26 # include <sysexits.h>
27 #else
28 # define EX_OK EXIT_SUCCESS
29 # define EX_OSERR EXIT_FAILURE
30 # define EX_UNAVAILABLE (EXIT_FAILURE + 1)
31 #endif
32
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
33 /* Check whether supplied process data matches session-wide dbus
34 * instance. */
3966b08 @mgorny Don't rely on stdbool as it was used in a single function only.
authored
35 int validateproc(proc_t* const p) {
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
36 const char *procname;
37 char* const *ap;
38 if (!p->cmdline)
3966b08 @mgorny Don't rely on stdbool as it was used in a single function only.
authored
39 return 0;
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
40
41 /* Check whether the binary name matches. */
42 procname = basename(p->cmdline[0]);
43 if (strcmp(procname, "dbus-daemon"))
3966b08 @mgorny Don't rely on stdbool as it was used in a single function only.
authored
44 return 0;
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
45
46 /* Lookup supplied command-line argument list for '--session'.
47 * We don't have to worry about additional '--system' arguments as
48 * dbus refuses to run with multiple configuration files supplied.
49 */
50 for (ap = &(p->cmdline[1]); *ap; ap++)
51 if (!strcmp(*ap, "--session"))
3966b08 @mgorny Don't rely on stdbool as it was used in a single function only.
authored
52 return 1;
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
53
3966b08 @mgorny Don't rely on stdbool as it was used in a single function only.
authored
54 return 0;
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
55 }
56
57 /* Lookup the process environment for specified keystr (a key name with
58 * terminating "=" and return the corresponding key=value suitable for
59 * passing to putenv(). If no key matches, return NULL. */
60 char *_findenv(const proc_t* const p, const char* const keystr) {
61 char* const *ap;
62 const int matchlen = strlen(keystr);
63
6a84b8f @mgorny Fix segv when p->environ is NULL.
authored
64 if (!p->environ)
65 return NULL;
66
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
67 for (ap = p->environ; *ap; ap++) {
68 if (!strncmp(*ap, keystr, matchlen))
69 return *ap;
70 }
71
72 return NULL;
73 }
74
75 /* Get the /proc/<pid>/root path suitable for chroot() call or return
76 * NULL if chroot not possible/necessary. */
77 const char* getroot(int pid) {
2a48885 @mgorny Make chroot() availability non-obligatory.
authored
78 #ifdef HAVE_CHROOT
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
79 static char fnbuf[11 + sizeof(pid) * 3];
80 char rlbuf[2];
81 snprintf(fnbuf, sizeof(fnbuf), "/proc/%d/root", pid);
82 if (readlink(fnbuf, rlbuf, sizeof(rlbuf)) != 1 || rlbuf[0] != '/')
83 return fnbuf;
84 else
2a48885 @mgorny Make chroot() availability non-obligatory.
authored
85 #endif
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
86 return NULL;
87 }
88
a4a44cf @mgorny Send notification only once per DISPLAY.
authored
89 struct used_bus {
90 char *display;
91
92 struct used_bus *next;
93 };
94
95 struct used_bus *used_buses = NULL;
96
97 void free_used_buses(void) {
98 struct used_bus *ub, *next_ub;
99
100 for (ub = used_buses; ub; ub = next_ub) {
101 next_ub = ub->next;
102 free(ub->display);
103 free(ub);
104 }
105 }
106
31203fc @mgorny Transform macro names uppercase.
authored
107 #define FINDENV(p, key) _findenv(p, key "=")
108 #define CANFAIL(expr) if (expr) perror(#expr " failed (ignoring)")
109 #define CANTFAIL(expr) if (expr) { perror(#expr " failed (aborting)"); exit(1); }
110
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
111 /* Fork and call notify-send for particular dbus session. */
d471577 @mgorny Check for fork() failure.
authored
112 int send_notify(char* const display, char* const xauth,
79978cc @mgorny Migrate to using libtinynotify.
authored
113 uid_t uid, const char* const root, NotifySession s, Notification n) {
114 int ret = 0;
5fd7904 @mgorny Do a complete UID restore with getresuid()+setresuid().
authored
115 uid_t old_ruid, old_euid, old_suid;
a4a44cf @mgorny Send notification only once per DISPLAY.
authored
116 struct used_bus *ub;
117
118 for (ub = used_buses; ub; ub = ub->next) {
119 if (!strcmp(ub->display, display))
120 return EX_OK;
121 }
5fd7904 @mgorny Do a complete UID restore with getresuid()+setresuid().
authored
122
123 CANTFAIL(getresuid(&old_ruid, &old_euid, &old_suid));
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
124
2a48885 @mgorny Make chroot() availability non-obligatory.
authored
125 #ifdef HAVE_CHROOT
79978cc @mgorny Migrate to using libtinynotify.
authored
126 if (root)
127 CANFAIL(chroot(root));
2a48885 @mgorny Make chroot() availability non-obligatory.
authored
128 #endif
5fd7904 @mgorny Do a complete UID restore with getresuid()+setresuid().
authored
129 CANFAIL(setresuid(uid, uid, old_euid));
79978cc @mgorny Migrate to using libtinynotify.
authored
130
131 CANTFAIL(putenv(display));
132 CANTFAIL(putenv(xauth));
133
134 notify_session_disconnect(s); /* ensure to get new connection */
a4a44cf @mgorny Send notification only once per DISPLAY.
authored
135 if (!notification_send(n, s)) {
136 ub = malloc(sizeof(*ub));
137 if (ub) {
138 ub->display = strdup(display);
139 if (!ub->display)
140 free(ub);
141 else {
142 ub->next = used_buses;
143 used_buses = ub;
144 }
145 }
79978cc @mgorny Migrate to using libtinynotify.
authored
146 ret = 1;
a4a44cf @mgorny Send notification only once per DISPLAY.
authored
147 }
79978cc @mgorny Migrate to using libtinynotify.
authored
148
5fd7904 @mgorny Do a complete UID restore with getresuid()+setresuid().
authored
149 CANTFAIL(setresuid(old_ruid, old_euid, old_suid));
79978cc @mgorny Migrate to using libtinynotify.
authored
150 #ifdef HAVE_CHROOT
151 if (root)
152 CANTFAIL(chroot(".")); /* escape the chroot */
153 #endif
154
155 return ret;
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
156 }
157
79978cc @mgorny Migrate to using libtinynotify.
authored
158 int main(int argc, char* argv[]) {
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
159 /* We need the command line and environment. Username would be nice
160 * too but readproc() shortens it and we need to getpwuid() anyway. */
79978cc @mgorny Migrate to using libtinynotify.
authored
161 PROCTAB *proc;
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
162 proc_t *p = NULL;
335e2c7 @mgorny Use <sysexits.h> whenever available.
authored
163 int ret = EX_UNAVAILABLE;
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
164
79978cc @mgorny Migrate to using libtinynotify.
authored
165 NotifySession s;
166 Notification n;
167
168 CANTFAIL(chdir("/"));
169
170 n = notification_new_from_cmdline(argc, argv, PACKAGE " " VERSION);
171 if (!n)
172 return EX_USAGE;
173
174 proc = openproc(PROC_FILLCOM | PROC_FILLENV);
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
175 if (!proc) {
176 fputs("FATAL: openproc() failed", stderr);
79978cc @mgorny Migrate to using libtinynotify.
authored
177 notification_free(n);
335e2c7 @mgorny Use <sysexits.h> whenever available.
authored
178 return EX_OSERR;
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
179 }
180
79978cc @mgorny Migrate to using libtinynotify.
authored
181 s = notify_session_new(PACKAGE, NULL);
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
182 while (((p = readproc(proc, p)))) {
183 if (validateproc(p)) {
31203fc @mgorny Transform macro names uppercase.
authored
184 char* const display = FINDENV(p, "DISPLAY");
185 char* xauth = FINDENV(p, "XAUTHORITY");
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
186 const struct passwd* const pw = getpwuid(p->euid);
187 char *xauthbuf = NULL;
188
189 if (!display || !pw || !(pw->pw_dir)) /* obligatory */
190 continue;
191 if (!xauth) { /* default to ~/.Xauthority */
192 const int bufsize = strlen(pw->pw_dir) + 25;
193 xauthbuf = calloc(bufsize, sizeof(*xauthbuf));
194 if (!xauthbuf) {
195 fprintf(stderr, "calloc() failed to allocate %dx%lud"
196 "bytes of memory for xauthbuf", bufsize, sizeof(*xauthbuf));
197 continue;
198 }
199 snprintf(xauthbuf, bufsize, "XAUTHORITY=%s/.Xauthority", pw->pw_dir);
200 xauth = xauthbuf;
201 }
202
79978cc @mgorny Migrate to using libtinynotify.
authored
203 if (send_notify(display, xauth, p->euid, getroot(p->tgid), s, n))
204 ret = EX_OK;
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
205
206 if (xauthbuf)
207 free(xauthbuf);
208 }
209 }
210
a4a44cf @mgorny Send notification only once per DISPLAY.
authored
211 free_used_buses();
212
79978cc @mgorny Migrate to using libtinynotify.
authored
213 notify_session_free(s);
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
214 closeproc(proc);
79978cc @mgorny Migrate to using libtinynotify.
authored
215 notification_free(n);
d471577 @mgorny Check for fork() failure.
authored
216 return ret;
7f4ad6d Introducing system-wide notify-send wrapper.
Michał Górny authored
217 }
Something went wrong with that request. Please try again.