awesomeConfig: test for execinfo.h/backtrace()
[awesome.git] / spawn.c
blob8303fac9d5bbe4545da361a4159b67bd4d88f9d0
1 /*
2 * spawn.c - Lua configuration management
4 * Copyright © 2009 Julien Danjou <julien@danjou.info>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <unistd.h>
23 #include <errno.h>
24 #include <sys/types.h>
25 #include <sys/wait.h>
27 #include <glib/gspawn.h>
29 #include "spawn.h"
30 #include "screen.h"
31 #include "luaa.h"
32 #include "event.h"
34 /** 20 seconds timeout */
35 #define AWESOME_SPAWN_TIMEOUT 20.0
37 /** Wrapper for unrefing startup sequence.
39 static inline void
40 a_sn_startup_sequence_unref(SnStartupSequence **sss)
42 return sn_startup_sequence_unref(*sss);
45 DO_ARRAY(SnStartupSequence *, SnStartupSequence, a_sn_startup_sequence_unref)
47 /** The array of startup sequence running */
48 SnStartupSequence_array_t sn_waits;
50 /** Remove a SnStartupSequence pointer from an array and forget about it.
51 * \param s The startup sequence to found, remove and unref.
52 * \return True if found and removed.
54 static inline bool
55 spawn_sequence_remove(SnStartupSequence *s)
57 for(int i = 0; i < sn_waits.len; i++)
58 if(sn_waits.tab[i] == s)
60 SnStartupSequence_array_take(&sn_waits, i);
61 sn_startup_sequence_unref(s);
62 return true;
64 return false;
67 static void
68 spawn_monitor_timeout(struct ev_loop *loop, ev_timer *w, int revents)
70 if(spawn_sequence_remove(w->data))
72 signal_t *sig = signal_array_getbyid(&global_signals,
73 a_strhash((const unsigned char *) "spawn::timeout"));
74 if(sig)
76 /* send a timeout signal */
77 lua_createtable(globalconf.L, 0, 2);
78 lua_pushstring(globalconf.L, sn_startup_sequence_get_id(w->data));
79 lua_setfield(globalconf.L, -2, "id");
80 foreach(func, sig->sigfuncs)
82 lua_pushvalue(globalconf.L, -1);
83 luaA_object_push(globalconf.L, (void *) *func);
84 luaA_dofunction(globalconf.L, 1, 0);
86 lua_pop(globalconf.L, 1);
89 sn_startup_sequence_unref(w->data);
90 p_delete(&w);
93 static void
94 spawn_monitor_event(SnMonitorEvent *event, void *data)
96 SnStartupSequence *sequence = sn_monitor_event_get_startup_sequence(event);
97 SnMonitorEventType event_type = sn_monitor_event_get_type(event);
99 lua_createtable(globalconf.L, 0, 2);
100 lua_pushstring(globalconf.L, sn_startup_sequence_get_id(sequence));
101 lua_setfield(globalconf.L, -2, "id");
103 const char *event_type_str = NULL;
105 switch(event_type)
107 case SN_MONITOR_EVENT_INITIATED:
108 /* ref the sequence for the array */
109 sn_startup_sequence_ref(sequence);
110 SnStartupSequence_array_append(&sn_waits, sequence);
111 event_type_str = "spawn::initiated";
113 /* Add a timeout function so we do not wait for this event to complete
114 * for ever */
115 struct ev_timer *ev_timeout = p_new(struct ev_timer, 1);
116 ev_timer_init(ev_timeout, spawn_monitor_timeout, AWESOME_SPAWN_TIMEOUT, 0.);
117 /* ref the sequence for the callback event */
118 sn_startup_sequence_ref(sequence);
119 ev_timeout->data = sequence;
120 ev_timer_start(globalconf.loop, ev_timeout);
121 break;
122 case SN_MONITOR_EVENT_CHANGED:
123 event_type_str = "spawn::change";
124 break;
125 case SN_MONITOR_EVENT_COMPLETED:
126 event_type_str = "spawn::completed";
127 break;
128 case SN_MONITOR_EVENT_CANCELED:
129 event_type_str = "spawn::canceled";
130 break;
133 /* common actions */
134 switch(event_type)
136 case SN_MONITOR_EVENT_INITIATED:
137 case SN_MONITOR_EVENT_CHANGED:
139 const char *s = sn_startup_sequence_get_name(sequence);
140 if(s)
142 lua_pushstring(globalconf.L, s);
143 lua_setfield(globalconf.L, -2, "name");
146 if((s = sn_startup_sequence_get_description(sequence)))
148 lua_pushstring(globalconf.L, s);
149 lua_setfield(globalconf.L, -2, "description");
152 lua_pushnumber(globalconf.L, sn_startup_sequence_get_workspace(sequence));
153 lua_setfield(globalconf.L, -2, "workspace");
155 if((s = sn_startup_sequence_get_binary_name(sequence)))
157 lua_pushstring(globalconf.L, s);
158 lua_setfield(globalconf.L, -2, "binary_name");
161 if((s = sn_startup_sequence_get_icon_name(sequence)))
163 lua_pushstring(globalconf.L, s);
164 lua_setfield(globalconf.L, -2, "icon_name");
167 if((s = sn_startup_sequence_get_wmclass(sequence)))
169 lua_pushstring(globalconf.L, s);
170 lua_setfield(globalconf.L, -2, "wmclass");
173 break;
174 case SN_MONITOR_EVENT_COMPLETED:
175 case SN_MONITOR_EVENT_CANCELED:
176 spawn_sequence_remove(sequence);
177 break;
180 /* send the signal */
181 signal_t *sig = signal_array_getbyid(&global_signals,
182 a_strhash((const unsigned char *) event_type_str));
184 if(sig)
186 foreach(func, sig->sigfuncs)
188 lua_pushvalue(globalconf.L, -1);
189 luaA_object_push(globalconf.L, (void *) *func);
190 luaA_dofunction(globalconf.L, 1, 0);
192 lua_pop(globalconf.L, 1);
196 /** Tell the spawn module that an app has been started.
197 * \param c The client that just started.
198 * \param startup_id The startup id of the started application.
200 void
201 spawn_start_notify(client_t *c, const char * startup_id)
203 foreach(_seq, sn_waits)
205 SnStartupSequence *seq = *_seq;
206 bool found = false;
207 const char *seqid = sn_startup_sequence_get_id(seq);
209 if(!a_strcmp(seqid, startup_id))
210 found = true;
211 else
213 const char *seqclass = sn_startup_sequence_get_wmclass(seq);
214 if(!a_strcmp(seqclass, c->class) || !a_strcmp(seqclass, c->instance))
215 found = true;
216 else
218 const char *seqbin = sn_startup_sequence_get_binary_name(seq);
219 if(!a_strcasecmp(seqbin, c->class) || !a_strcasecmp(seqbin, c->instance))
220 found = true;
224 if(found)
226 sn_startup_sequence_complete(seq);
227 break;
232 /** Initialize program spawner.
234 void
235 spawn_init(void)
237 globalconf.sndisplay = sn_xcb_display_new(globalconf.connection, NULL, NULL);
239 const int screen_max = xcb_setup_roots_length(xcb_get_setup(globalconf.connection));
241 for(int screen = 0; screen < screen_max; screen++)
242 globalconf.screens.tab[screen].snmonitor = sn_monitor_context_new(globalconf.sndisplay,
243 screen,
244 spawn_monitor_event,
245 NULL, NULL);
248 static void
249 spawn_launchee_timeout(struct ev_loop *loop, ev_timer *w, int revents)
251 sn_launcher_context_complete(w->data);
252 sn_launcher_context_unref(w->data);
253 p_delete(&w);
256 /** This callback is executed in the child process after fork(). It clears the
257 * signal mask. Without this, child processes would be started with e.g.
258 * SIGINT blocked if we are compiled against libev 3.8 or newer.
259 * BEWARE: Pending signals are inherited by child processes. This might mean
260 * that we receive a signal that was meant for awesome after clearing the signal
261 * mask! Sadly, we can't do anything about this.
262 * \param user_data Not used.
264 static void
265 spawn_child_callback(gpointer user_data)
267 sigset_t empty;
269 sigemptyset(&empty);
270 sigprocmask(SIG_SETMASK, &empty, NULL);
273 /** Spawn a command.
274 * \param command_line The command line to launch.
275 * \param error A error pointer to fill with the possible error from
276 * g_spawn_async.
277 * \return g_spawn_async value.
279 static GPid
280 spawn_command(const gchar *command_line, GError **error)
282 gboolean retval;
283 GPid pid;
284 gchar **argv = 0;
286 if(!g_shell_parse_argv(command_line, NULL, &argv, error))
287 return 0;
289 retval = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
290 spawn_child_callback, NULL, &pid, error);
291 g_strfreev (argv);
293 if (!retval)
294 return 0;
295 return pid;
298 /** Spawn a program.
299 * This function is multi-head (Zaphod) aware and will set display to
300 * the right screen according to mouse position.
301 * \param L The Lua VM state.
302 * \return The number of elements pushed on stack
303 * \luastack
304 * \lparam The command to launch.
305 * \lparam Use startup-notification, true or false, default to true.
306 * \lparam The optional screen number to spawn the command on.
307 * \lreturn Process ID if everything is OK, or an error string if an error occured.
310 luaA_spawn(lua_State *L)
312 char *host, newdisplay[128];
313 const char *cmd;
314 bool use_sn = true;
315 int screen = 0, screenp, displayp;
317 if(lua_gettop(L) >= 2)
318 use_sn = luaA_checkboolean(L, 2);
320 if(lua_gettop(L) == 3)
322 screen = luaL_checknumber(L, 3) - 1;
323 luaA_checkscreen(screen);
326 cmd = luaL_checkstring(L, 1);
328 if(!globalconf.xinerama_is_active)
330 xcb_parse_display(NULL, &host, &displayp, &screenp);
331 snprintf(newdisplay, sizeof(newdisplay), "%s:%d.%d", host, displayp, screen);
332 setenv("DISPLAY", newdisplay, 1);
333 p_delete(&host);
336 SnLauncherContext *context = NULL;
337 if(use_sn)
339 char *cmdname, *space;
340 const char *first_no_space_char = cmd;
341 /* Look for the first char which is not space */
342 while(*first_no_space_char && *first_no_space_char == ' ')
343 first_no_space_char++;
344 /* Look for space in the string to get the command name. */
345 if((space = strchr(first_no_space_char, ' ')))
346 cmdname = a_strndup(cmd, space - cmd);
347 else
348 cmdname = a_strdup(cmd);
350 context = sn_launcher_context_new(globalconf.sndisplay, screen_virttophys(screen));
351 sn_launcher_context_set_name(context, "awesome");
352 sn_launcher_context_set_description(context, "awesome spawn");
353 sn_launcher_context_set_binary_name(context, cmdname);
354 sn_launcher_context_initiate(context, "awesome", cmdname, globalconf.timestamp);
355 p_delete(&cmdname);
357 /* app will have AWESOME_SPAWN_TIMEOUT seconds to complete,
358 * or the timeout function will terminate the launch sequence anyway */
359 struct ev_timer *ev_timeout = p_new(struct ev_timer, 1);
360 ev_timer_init(ev_timeout, spawn_launchee_timeout, AWESOME_SPAWN_TIMEOUT, 0.);
361 ev_timeout->data = context;
362 ev_timer_start(globalconf.loop, ev_timeout);
363 sn_launcher_context_setup_child_process(context);
366 GError *error = NULL;
367 GPid pid = spawn_command(cmd, &error);
368 if(!pid)
370 /* push error on stack */
371 lua_pushstring(L, error->message);
372 g_error_free(error);
373 if(context)
374 sn_launcher_context_complete(context);
375 return 1;
378 /* push pid on stack */
379 lua_pushnumber(L, pid);
381 return 1;
384 /** Spawn a program. This works exactly as system() does, but clears the signal mask.
385 * \param arg The shell command to execute.
386 * \return The return status of the program.
389 spawn_system(const char *arg)
391 GError *error;
392 gboolean retval;
393 gint status;
394 static const char *shell = NULL;
395 if(!shell && !(shell = getenv("SHELL")))
396 shell = "/bin/sh";
397 char *argv[] = { (char *) shell, (char *) "-c", (char *) arg, NULL };
399 retval = g_spawn_sync(NULL, argv, NULL, 0, spawn_child_callback,
400 NULL, NULL, NULL, &status, &error);
402 if (retval)
403 return status;
405 g_error_free(error);
406 return -1;
409 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80