libcfe  0.12.1
some useful C-functions
knotify.c
Go to the documentation of this file.
1 #include "config.h"
2 #include "knotify.h"
3 
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <stddef.h>
7 #include <dirent.h>
8 #include <string.h>
9 #include <libintl.h>
10 
11 #ifdef WITH_KNOTIFY
12 #ifdef HAVE_GIO_GIO_H
13 #define INCLUDE_KDENOTIFY
14 #include <gio/gio.h>
15 #endif /* HAVE_GIO_GIO_H */
16 #endif /* WITH_KNOTIFY */
17 
18 #include "output.h"
19 #include "len.h"
20 #include "char_to_int.h"
21 #include "check_path_exists.h"
22 
23 #define UNUSED(x) (void)(x)
24 
25 #ifndef MIN
26 # define MIN(a,b) (((a) < (b)) ? (a) : (b))
27 #endif
28 
29 int check(const struct dirent *e);
30 uid_t find_dbus_user(void);
31 
32 static char *default_app = "kde";
33 
34 int check(const struct dirent *e)
35 {
36  if(strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0)
37  return 0;
38  if(e->d_type == DT_DIR)
39  return 1;
40  return 0;
41 }
42 
43 uid_t find_dbus_user(void)
44 {
45  const char *machine_id_file = "/var/lib/dbus/machine-id";
46  const char *search_string = "DBUS_SESSION_BUS_PID=";
47  const int search_string_len = str_len(search_string);
48  // get machine id from /var/lib/dbus/machine-id
49 
50  if(check_file_exists(machine_id_file, PATH_R) != 0)
51  return (uid_t)0;
52 
53  char *machine_id = NULL;
54  size_t n = 0;
55  FILE *fp = fopen(machine_id_file, "r");
56  getline(&machine_id, &n, fp);
57  fclose(fp);
58 
59  if(machine_id[line_len(machine_id)] == '\n')
60  machine_id[line_len(machine_id)] = '\0';
61 
62  // search for file /home/*/.dbus/session-bus/$machine_id-0
63  struct dirent **dirs = NULL;
64  int j, k;
65 
66  if((j = scandir("/home/", &dirs, check, NULL)) != -1)
67  {
68  k = 0;
69  while(k < j)
70  {
71  char *file = NULL;
72  asprintf(&file, "/home/%s/.dbus/session-bus/%s-0", dirs[k]->d_name, machine_id);
73  k++;
74  if(check_file_exists(file, PATH_R) != 0)
75  continue;
76 
77  // read file
78  char *line = NULL;
79  n = 0;
80  ssize_t s;
81  fp = fopen(file, "r");
82 
83  // read file /home/*/.dbus/session-bus/$machine_id-0
84  while((s = getline(&line, &n, fp)) != -1)
85  {
86  // search for a line starting with 'DBUS_SESSION_BUS_PID'
87  if(strncmp(line, search_string, search_string_len) == 0)
88  {
89  // translate the 2nd part to a number
90  pid_t pid = (pid_t)char_to_int(line + search_string_len, MIN(str_len(line), line_len(line)) - search_string_len, 10);
91  char *proc_dir = NULL;
92  // lookup the owner of this process id
93  asprintf(&proc_dir, "/proc/%d/", pid);
94 
95  if(check_dir_exists(proc_dir, PATH_R) != 0)
96  {
97  free(proc_dir);
98  proc_dir = NULL;
99  continue;
100  }
101 
102  struct stat st;
103  if(stat(proc_dir, &st) != 0)
104  {
105  free(proc_dir);
106  proc_dir = NULL;
107  continue;
108  }
109  free(proc_dir);
110  proc_dir = NULL;
111  return st.st_uid;
112  }
113 
114  }
115  free(line);
116  line = NULL;
117  n = 0;
118  fclose(fp);
119  fp = NULL;
120  }
121  }
122 
123  // return DBUS_SESSION_BUS_PID
124 
125  return (uid_t)0;
126 }
127 
128 int kdenotify(char *app, char *title, char *text)
129 {
130 #ifdef INCLUDE_KDENOTIFY
131  GError *error = NULL;
132 
133  if(app == NULL)
134  app = default_app;
135  if(text == NULL || title == NULL)
136  return -1;
137 
138  unsigned int timeout = 5000; // duration of message in miliseconds
139 
140  if(getuid() == 0)
141  {
142  // get the dbus session user
143  uid_t dbus_id = find_dbus_user();
144 
145  // change to the dbus session user
146  debug(_("change effective user id to %d for dbus call"), dbus_id);
147  seteuid(dbus_id);
148  }
149 
150  g_type_init();
151  GDBusProxy *proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, NULL, "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", NULL, &error);
152  if(proxy == NULL)
153  {
154  error(_("Error creating proxy: %s"), error->message);
155  g_error_free(error);
156  return -1;
157  }
158  /*
159  * s application name
160  * u rplaces_id
161  * s app_icon
162  * s summary (/title)
163  * s message
164  * as actions
165  * a{sv} hints
166  * i timeout
167  */
168 
169  GVariant *data = g_variant_new("(susssasa{sv}i)", app, 0, "", title, text, NULL, NULL, timeout);
170  GVariant *res = g_dbus_proxy_call_sync(proxy, "org.freedesktop.Notifications.Notify", data, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
171  if(res == NULL)
172  {
173  error(_("Error issuing call: %s"), error->message);
174  g_error_free(error);
175  return -1;
176  }
177 
178  if(getuid() != geteuid())
179  {
180  debug(_("change effective user id back to root"));
181  seteuid(0);
182  }
183 #else
184  UNUSED(app);
185  UNUSED(title);
186  UNUSED(text);
187  UNUSED(default_app);
188 #endif /* INCLUDE_KDENOTIFY */
189  return 0;
190 }
#define PATH_R
request read permission
uid_t find_dbus_user(void)
Definition: knotify.c:43
#define str_len(s)
Shorthand for counting '\0' terminating strings. See _len for more info.
Definition: len.h:17
#define _(string)
Definition: output.h:43
#define check_file_exists(file, mode)
#define check_dir_exists(dir, mode)
#define UNUSED(x)
Used to indicate that a function does not use a certain parameter.
Definition: knotify.c:23
#define error(...)
Definition: output.h:30
#define debug(...)
Definition: output.h:38
int check(const struct dirent *e)
Definition: knotify.c:34
#define line_len(s)
Shorthand for counting '\n' terminating strings. See _len for more info.
Definition: len.h:18
unsigned long long char_to_int(const char *s, int i, uint8_t base)
Definition: char_to_int.c:24
#define MIN(a, b)
Definition: knotify.c:26
int kdenotify(char *app, char *title, char *text)
Definition: knotify.c:128