/* * LASH * * Copyright (C) 2002, 2003 Robert Ham * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include extern int h_errno; #include #include #include #include #include #include #include lash_args_t * lash_extract_args(int *argc, char ***argv) { int i, j, valid_count; lash_args_t *args; args = lash_args_new(); for (i = 1; i < *argc; ++i) { if (strncasecmp("--lash-server=", (*argv)[i], 14) == 0) { LASH_DEBUGARGS("setting server from command line: '%s'", (*argv)[i] + 14); lash_args_set_server(args, (*argv)[i] + 14); (*argv)[i] = NULL; continue; } if (strncasecmp("--lash-project=", (*argv)[i], 15) == 0) { LASH_DEBUGARGS("setting project from command line: '%s'", (*argv)[i] + 15); lash_args_set_project(args, (*argv)[i] + 15); (*argv)[i] = NULL; continue; } if (strncmp("--lash-id=", (*argv)[i], 10) == 0) { uuid_t id; LASH_DEBUGARGS("setting id from command line: '%s'", (*argv)[i] + 10); j = uuid_parse((*argv)[i] + 10, id); LASH_PRINT_DEBUG("id parsed"); (*argv)[i] = NULL; if (j == -1) { fprintf(stderr, "%s: ERROR PARSING ID FROM COMMAND LINE! THIS IS BAD!\n", __FUNCTION__); } else { lash_args_set_id(args, id); } continue; } if (strncmp("--lash-no-autoresume", (*argv)[i], 20) == 0) { LASH_PRINT_DEBUG ("setting LASH_No_Autoresume flag from command line"); lash_args_set_flag(args, LASH_No_Autoresume); (*argv)[i] = NULL; continue; } if (strncmp("--lash-no-start-server", (*argv)[i], 22) == 0) { LASH_PRINT_DEBUG ("setting LASH_No_Start_Server flag from command line"); lash_args_set_flag(args, LASH_No_Start_Server); (*argv)[i] = NULL; continue; } } LASH_PRINT_DEBUG("args checked"); /* sort out the argv pointers */ valid_count = *argc; for (i = 1; i < valid_count; ++i) { LASH_DEBUGARGS("checking argv[%d]", i); if ((*argv)[i] == NULL) { for (j = i; j < *argc - 1; ++j) (*argv)[j] = (*argv)[j + 1]; valid_count--; i--; } } LASH_PRINT_DEBUG("done"); *argc = valid_count; lash_args_set_args(args, *argc, *argv); return args; } lash_client_t * lash_init(const lash_args_t * args, const char *class, int client_flags, lash_protocol_t protocol) { lash_connect_params_t *connect_params; lash_client_t *client; int err; char *str; const char *cstr; char wd[MAXPATHLEN]; int tries; uuid_t id; client = lash_client_new(); connect_params = lash_connect_params_new(); client->args = lash_args_duplicate(args); client->args->flags |= client_flags; lash_client_set_class(client, class); str = getcwd(wd, MAXPATHLEN); if (!str) { fprintf(stderr, "%s: could not get current working directory: %s\n", __FUNCTION__, strerror(errno)); str = getenv("PWD"); if (str) lash_connect_params_set_working_dir(connect_params, str); else lash_connect_params_set_working_dir(connect_params, getenv("HOME")); } else { lash_connect_params_set_working_dir(connect_params, str); } LASH_DEBUGARGS("protocol version for connect: %s", lash_protocol_string(protocol)); connect_params->protocol_version = protocol; connect_params->flags = client->args->flags; lash_connect_params_set_project(connect_params, args->project); lash_connect_params_set_class(connect_params, class); uuid_copy(connect_params->id, args->id); connect_params->argc = args->argc; connect_params->argv = args->argv; /* try and connect to the server */ LASH_PRINT_DEBUG("connecting to server"); cstr = lash_args_get_server(args); err = lash_comm_connect_to_server(client, cstr ? cstr : "localhost", LASH_PORT, connect_params); /* couldn't connect, try to start a new server */ /* but not if this client has been started by a server, in which case something must be broken if we can't connect */ if ( !(client_flags & LASH_No_Start_Server) ) { lash_args_get_id(args, id); if (err && getenv("LASH_NO_START_SERVER") == NULL && uuid_is_null(id)) { LASH_DEBUGARGS("%s: trying to start new LASH server\n", __FUNCTION__); /* using the same double fork() trick as JACK does to prevent zombie children */ err = fork(); /* child process will run this statement */ if (err == 0) { /* need to close all open file descriptors except the std ones */ int max_fds = getdtablesize(); int fd; for (fd = 3; fd < max_fds; ++fd) close(fd); switch (fork()) { /* grandchild process will run this block */ case 0: setsid(); execlp("lashd", "lashd", NULL); _exit(-1); /* this block only runs if the second fork() fails */ case -1: _exit (-1); /* exit the child process here */ default: _exit (0); } } /* if the fork succeeded, try to connect to the new server */ else if (err > 0) { waitpid(err, NULL, 0); for (tries = 0; tries < 5; ++tries) { sleep(1); err = lash_comm_connect_to_server(client, cstr ? cstr : "localhost", LASH_PORT, connect_params); if (err == 0) { LASH_PRINT_DEBUG("successfully launched and connected to lashd"); break; } } /* fork failed */ } else { fprintf(stderr, "%s: fork failed while starting new server: %s\n", __FUNCTION__, strerror(err)); } } else { fprintf(stderr, "%s: Not attempting to start daemon server automatically\n", __FUNCTION__, __FUNCTION__); } } // this deletes the contained strings, but we don't want to do that // since they point to the strings in args //lash_connect_params_destroy(connect_params); connect_params = NULL; if (err) { fprintf(stderr, "%s: could not connect to server '%s' - disabling LASH\n", __FUNCTION__, cstr ? cstr : "localhost"); lash_client_destroy(client); return NULL; } LASH_PRINT_DEBUG("connected to server"); client->server_connected = 1; err = pthread_create(&client->recv_thread, NULL, lash_comm_recv_run, client); if (err) { fprintf(stderr, "%s: error creating recieve thread - disabling LASH: %s\n", __FUNCTION__, strerror(err)); lash_client_destroy(client); return NULL; } err = pthread_create(&client->send_thread, NULL, lash_comm_send_run, client); if (err) { fprintf(stderr, "%s: error creating send thread - disabling LASH: %s\n", __FUNCTION__, strerror(err)); client->recv_close = 1; pthread_join(client->recv_thread, NULL); lash_client_destroy(client); return NULL; } return client; } unsigned int lash_get_pending_event_count(lash_client_t * client) { unsigned int count = 0; if (!client) return 0; pthread_mutex_lock(&client->events_in_lock); if (client->events_in) count = lash_list_length(client->events_in); pthread_mutex_unlock(&client->events_in_lock); return count; } unsigned int lash_get_pending_config_count(lash_client_t * client) { unsigned int count = 0; if (!lash_enabled(client)) return 0; pthread_mutex_lock(&client->configs_in_lock); if (client->events_in) count = lash_list_length(client->configs_in); pthread_mutex_unlock(&client->configs_in_lock); return count; } lash_event_t * lash_get_event(lash_client_t * client) { lash_event_t *event = NULL; if (!client) return NULL; pthread_mutex_lock(&client->events_in_lock); if (client->events_in) { event = (lash_event_t *) client->events_in->data; client->events_in = lash_list_remove(client->events_in, event); } pthread_mutex_unlock(&client->events_in_lock); return event; } lash_config_t * lash_get_config(lash_client_t * client) { lash_config_t *config = NULL; if (!client) return NULL; pthread_mutex_lock(&client->configs_in_lock); if (client->configs_in) { config = (lash_config_t *) client->configs_in->data; client->configs_in = lash_list_remove(client->configs_in, config); } pthread_mutex_unlock(&client->configs_in_lock); return config; } void lash_send_comm_event(lash_client_t * client, lash_comm_event_t * event) { pthread_mutex_lock(&client->comm_events_out_lock); client->comm_events_out = lash_list_append(client->comm_events_out, event); pthread_mutex_unlock(&client->comm_events_out_lock); pthread_cond_signal(&client->send_conditional); } void lash_send_event(lash_client_t * client, lash_event_t * event) { lash_comm_event_t *comm_event; if (!lash_enabled(client)) { lash_event_destroy(event); return; } comm_event = lash_malloc(sizeof(lash_comm_event_t)); comm_event->type = LASH_Comm_Event_Event; comm_event->event_data.event = event; lash_send_comm_event(client, comm_event); } void lash_send_config(lash_client_t * client, lash_config_t * config) { lash_comm_event_t *comm_event; if (!lash_enabled(client)) { lash_config_destroy(config); return; } comm_event = lash_malloc(sizeof(lash_comm_event_t)); comm_event->type = LASH_Comm_Event_Config; comm_event->event_data.config = config; lash_send_comm_event(client, comm_event); } const char * lash_get_server_name(lash_client_t * client) { if (!lash_enabled(client)) return NULL; return lash_lookup_peer_name(client->socket); } int lash_server_connected(lash_client_t * client) { if (!client) return 0; return client->server_connected; } void lash_jack_client_name(lash_client_t * client, const char *name) { lash_event_t *event; if (!lash_enabled(client)) return; if (!name) return; event = lash_event_new_with_all(LASH_Jack_Client_Name, name); lash_send_event(client, event); } void lash_alsa_client_id(lash_client_t * client, unsigned char id) { lash_event_t *event; if (!lash_enabled(client)) return; if (!id) return; event = lash_event_new(); lash_event_set_alsa_client_id(event, id); lash_send_event(client, event); } /* EOF */