daemon: better handling for non-direct childs. Fixes #62

If the first JACK client that appears has pid different from the one
of the child process, send SIGUSR1 on L1 save to this first
grandchild.

This changeset fixes the "run L1 app in terminal" issue and the
similar issue with dash (the default shell on Debian and Ubuntu, that
for simple commandlines does fork() and exec() instead of just exec(),
like bash does. This changeset also fixes the situation with complex
commandlines that result in only one JACK client.

The complex commandlines that result in multiple processes creating
JACK clients are handled by sending the SIGUSR1 to the first process
that creates JACK client. In future this could be improved by sending
SIGUSR1 to all of them but it is probably better to avoid such
situations by creating one app per JACK-creating process.
This commit is contained in:
Nedko Arnaudov 2010-10-24 02:57:00 +03:00
parent 6e2e650bb5
commit 698bf81a3b
3 changed files with 124 additions and 56 deletions

View File

@ -46,6 +46,7 @@ struct ladish_app
bool terminal;
uint8_t level;
pid_t pid;
pid_t firstborn_pid;
bool zombie; /* if true, remove when stopped */
bool autorun;
unsigned int state;
@ -261,6 +262,7 @@ ladish_app_supervisor_add(
app_ptr->terminal = terminal;
app_ptr->level = level;
app_ptr->pid = 0;
app_ptr->firstborn_pid = 0;
app_ptr->id = supervisor_ptr->next_id++;
uuid_generate(app_ptr->uuid);
@ -288,15 +290,49 @@ ladish_app_supervisor_add(
return (ladish_app_handle)app_ptr;
}
static void ladish_app_send_signal(struct ladish_app * app_ptr, int sig)
static void ladish_app_send_signal(struct ladish_app * app_ptr, int sig, bool prefer_firstborn)
{
pid_t pid;
const char * signal_name;
ASSERT(app_ptr->state = LADISH_APP_STATE_STARTED);
if (app_ptr->pid <= 0)
{
ASSERT_NO_PASS;
return;
}
kill(app_ptr->pid, sig);
if (prefer_firstborn && app_ptr->firstborn_pid != 0)
{
pid = app_ptr->firstborn_pid;
}
else
{
pid = app_ptr->pid;
}
switch (sig)
{
case SIGTERM:
signal_name = "SIGTERM";
break;
case SIGKILL:
signal_name = "SIGKILL";
break;
case SIGUSR1:
signal_name = "SIGUSR1";
break;
default:
signal_name = strsignal(sig);
if (signal_name == NULL)
{
signal_name = "unknown";
}
}
log_info("sending signal %d (%s) to '%s' with pid %u", sig, signal_name, app_ptr->name, (unsigned int)pid);
kill(pid, sig);
}
void ladish_app_supervisor_clear(ladish_app_supervisor_handle supervisor_handle)
@ -317,7 +353,7 @@ void ladish_app_supervisor_clear(ladish_app_supervisor_handle supervisor_handle)
if (app_ptr->pid != 0)
{
log_info("terminating '%s'...", app_ptr->name);
ladish_app_send_signal(app_ptr, SIGTERM);
ladish_app_send_signal(app_ptr, SIGTERM, false);
app_ptr->zombie = true;
app_ptr->state = LADISH_APP_STATE_STOPPING;
}
@ -374,6 +410,7 @@ bool ladish_app_supervisor_child_exit(ladish_app_supervisor_handle supervisor_ha
log_info("exit of child '%s' detected.", app_ptr->name);
app_ptr->pid = 0;
app_ptr->firstborn_pid = 0;
if (app_ptr->zombie)
{
remove_app_internal(supervisor_ptr, app_ptr);
@ -419,6 +456,14 @@ ladish_app_supervisor_enum(
return true;
}
static inline void ladish_app_save_L1_internal(struct ladish_app * app_ptr)
{
if (app_ptr->level == 1)
{
ladish_app_send_signal(app_ptr, SIGUSR1, true);
}
}
#define app_ptr ((struct ladish_app *)app_handle)
bool ladish_app_supervisor_start_app(ladish_app_supervisor_handle supervisor_handle, ladish_app_handle app_handle)
@ -472,23 +517,49 @@ void ladish_app_get_uuid(ladish_app_handle app_handle, uuid_t uuid)
void ladish_app_stop(ladish_app_handle app_handle)
{
ladish_app_send_signal(app_ptr, SIGTERM);
ladish_app_send_signal(app_ptr, SIGTERM, false);
app_ptr->state = LADISH_APP_STATE_STOPPING;
}
void ladish_app_kill(ladish_app_handle app_handle)
{
ladish_app_send_signal(app_ptr, SIGKILL);
ladish_app_send_signal(app_ptr, SIGKILL, false);
app_ptr->state = LADISH_APP_STATE_KILL;
}
void ladish_app_save_L1(ladish_app_handle app_handle)
{
if (app_ptr->level == 1)
ladish_app_save_L1_internal(app_ptr);
}
void ladish_app_add_pid(ladish_app_handle app_handle, pid_t pid)
{
if (app_ptr->pid == 0)
{
log_info("sending SIGUSR1 to '%s' with pid %u", app_ptr->name, (unsigned int)app_ptr->pid);
ladish_app_send_signal(app_ptr, SIGUSR1);
log_error("Associating pid with stopped app does not make sense");
ASSERT_NO_PASS;
return;
}
if (pid <= 1) /* catch -1, 0 and 1 */
{
log_error("Refusing domination by ignoring pid %d", (int)pid);
ASSERT_NO_PASS;
return;
}
if (app_ptr->pid == pid)
{ /* The top level process that is already known */
return;
}
if (app_ptr->firstborn_pid != 0)
{ /* Ignore non-first children */
return;
}
log_info("First grandchild with pid %u", (unsigned int)pid);
app_ptr->firstborn_pid = pid;
}
#undef app_ptr
@ -530,7 +601,7 @@ void ladish_app_supervisor_stop(ladish_app_supervisor_handle supervisor_handle)
if (app_ptr->pid != 0)
{
log_info("terminating '%s'...", app_ptr->name);
ladish_app_send_signal(app_ptr, SIGTERM);
ladish_app_send_signal(app_ptr, SIGTERM, false);
app_ptr->autorun = true;
app_ptr->state = LADISH_APP_STATE_STOPPING;
}
@ -556,11 +627,7 @@ void ladish_app_supervisor_save_L1(ladish_app_supervisor_handle supervisor_handl
continue;
}
if (app_ptr->level == 1)
{
log_info("sending SIGUSR1 to '%s' with pid %u", app_ptr->name, (unsigned int)app_ptr->pid);
ladish_app_send_signal(app_ptr, SIGUSR1);
}
ladish_app_save_L1_internal(app_ptr);
}
}

View File

@ -368,6 +368,14 @@ void ladish_app_kill(ladish_app_handle app_handle);
*/
void ladish_app_save_L1(ladish_app_handle app_handle);
/**
* Associate pid with app.
*
* @param[in] app_handle Handle of app
* @param[in] pid PID to associate with the app
*/
void ladish_app_add_pid(ladish_app_handle app_handle, pid_t pid);
/**
* D-Bus interface descriptor for the app supervisor interface. The call context must be a ::ladish_app_supervisor_handle
*/

View File

@ -55,19 +55,19 @@ UUID_DEFINE(g_a2j_uuid,0xBE,0x23,0xA2,0x42,0xE2,0xB2,0x11,0xDE,0xB7,0x95,0x00,0x
struct app_find_context
{
pid_t pid;
char * app_name;
uuid_t app_uuid;
ladish_graph_handle graph;
ladish_app_handle app;
};
#define app_find_context_ptr ((struct app_find_context *)context)
static bool get_app_properties_from_supervisor(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
static bool lookup_app_in_supervisor(void * context, ladish_graph_handle graph, ladish_app_supervisor_handle app_supervisor)
{
pid_t pid;
ladish_app_handle app;
ASSERT(app_find_context_ptr->app_name == NULL); /* we stop iteration when app is found */
/* we stop iteration when app is found */
ASSERT(app_find_context_ptr->app == NULL && app_find_context_ptr->graph == NULL);
//log_info("checking app supervisor \"%s\" for pid %llu", ladish_app_supervisor_get_name(app_supervisor), (unsigned long long)pid);
@ -93,43 +93,34 @@ static bool get_app_properties_from_supervisor(void * context, ladish_graph_hand
return true; /* continue app supervisor iteration */
}
app_find_context_ptr->app_name = strdup(ladish_app_get_name(app));
if (app_find_context_ptr->app_name == NULL)
{
log_error("strdup() failed for app name '%s'", ladish_app_get_name(app));
}
else
{
ladish_app_get_uuid(app, app_find_context_ptr->app_uuid);
app_find_context_ptr->pid = pid;
app_find_context_ptr->graph = graph;
}
app_find_context_ptr->app = app;
app_find_context_ptr->graph = graph;
return false; /* stop app supervisor iteration */
}
#undef app_find_context_ptr
static char * get_app_properties(struct virtualizer * virtualizer_ptr, pid_t pid, ladish_graph_handle * graph_ptr, uuid_t app_uuid)
static ladish_app_handle ladish_virtualizer_find_app_by_pid(struct virtualizer * virtualizer_ptr, pid_t pid, ladish_graph_handle * graph_ptr)
{
struct app_find_context context;
context.pid = (pid_t)pid;
context.app_name = NULL;
context.pid = pid;
context.app = NULL;
context.graph = NULL;
uuid_clear(context.app_uuid);
ladish_studio_iterate_virtual_graphs(&context, get_app_properties_from_supervisor);
ladish_studio_iterate_virtual_graphs(&context, lookup_app_in_supervisor);
if (context.app_name != NULL)
{
ASSERT(context.graph != NULL);
ASSERT(!uuid_is_null(context.app_uuid));
*graph_ptr = context.graph;
uuid_copy(app_uuid, context.app_uuid);
if (context.app == NULL)
{ /* app not found */
ASSERT(context.graph == NULL);
return NULL;
}
return context.app_name;
ASSERT(context.graph != NULL);
*graph_ptr = context.graph;
return context.app;
}
struct find_link_port_context
@ -271,7 +262,7 @@ static void client_appeared(void * context, uint64_t id, const char * jack_name)
ladish_client_handle client;
const char * a2j_name;
bool is_a2j;
char * app_name;
ladish_app_handle app;
uuid_t app_uuid;
const char * name;
pid_t pid;
@ -284,7 +275,7 @@ static void client_appeared(void * context, uint64_t id, const char * jack_name)
is_a2j = a2j_name != NULL && strcmp(a2j_name, jack_name) == 0;
name = jack_name;
app_name = NULL;
app = NULL;
graph = NULL;
jmcore = false;
@ -305,11 +296,13 @@ static void client_appeared(void * context, uint64_t id, const char * jack_name)
}
else
{
app_name = get_app_properties(virtualizer_ptr, pid, &graph, app_uuid);
if (app_name != NULL)
app = ladish_virtualizer_find_app_by_pid(virtualizer_ptr, pid, &graph);
if (app != NULL)
{
log_info("app name is '%s'", app_name);
name = app_name;
ladish_app_get_uuid(app, app_uuid);
ASSERT(!uuid_is_null(app_uuid));
name = ladish_app_get_name(app);
log_info("app name is '%s'", name);
}
}
}
@ -337,7 +330,7 @@ static void client_appeared(void * context, uint64_t id, const char * jack_name)
if (ladish_client_get_jack_id(client) != 0)
{
log_error("Ignoring client with duplicate name '%s' ('%s')", name, jack_name);
goto free_app_name;
goto exit;
}
ladish_client_set_jack_id(client, id);
@ -349,7 +342,7 @@ static void client_appeared(void * context, uint64_t id, const char * jack_name)
if (!ladish_client_create(is_a2j ? g_a2j_uuid : NULL, &client))
{
log_error("ladish_client_create() failed. Ignoring client %"PRIu64" (%s)", id, jack_name);
goto free_app_name;
goto exit;
}
ladish_client_set_jack_id(client, id);
@ -358,7 +351,7 @@ static void client_appeared(void * context, uint64_t id, const char * jack_name)
{
log_error("ladish_graph_add_client() failed to add client %"PRIu64" (%s) to JACK graph", id, name);
ladish_client_destroy(client);
goto free_app_name;
goto exit;
}
done:
@ -367,10 +360,13 @@ done:
virtualizer_ptr->system_client_id = id;
}
if (app_name != NULL)
if (app != NULL)
{
/* interlink client and app */
ladish_app_add_pid(app, pid);
ladish_client_set_pid(client, pid);
ladish_client_set_app(client, app_uuid);
ASSERT(graph);
ladish_client_set_vgraph(client, graph);
virtualizer_ptr->our_clients_count++;
@ -386,11 +382,8 @@ done:
ladish_client_set_vgraph(client, g_studio.studio_graph);
}
free_app_name:
if (app_name != NULL)
{
free(app_name);
}
exit:
return;
}
static void client_disappeared(void * context, uint64_t id)