/* * LASH * * Copyright (C) 2002 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. */ #define _GNU_SOURCE #include "config.h" #include #include #include #include #include #include #include "server.h" #include "project.h" #include "client_event.h" void server_create_loader(server_t * server); void server_set_loader(server_t * server, loader_t * loader); void server_set_ui_project(server_t * server, const char *project); void server_set_default_dir(server_t * server, const char *default_dir); server_t * server_new(const char *default_dir) { server_t *server; server = lash_malloc0(sizeof(server_t)); pthread_mutex_init(&server->server_events_lock, NULL); pthread_cond_init(&server->server_event_cond, NULL); server->default_dir = lash_strdup(default_dir); server_create_loader(server); LASH_PRINT_DEBUG("starting jack, alsa and comm threads"); server->jack_mgr = jack_mgr_new(server); #ifdef HAVE_ALSA server->alsa_mgr = alsa_mgr_new(server); #endif server->conn_mgr = conn_mgr_new(server); if (!server->conn_mgr) exit(1); LASH_PRINT_DEBUG("threads started"); return server; } void server_destroy(server_t * server) { lash_list_t *node; for (node = server->projects; node; node = lash_list_next(node)) project_destroy((project_t *) node->data); lash_list_free(server->projects); LASH_PRINT_DEBUG("destroying connection manager"); conn_mgr_destroy(server->conn_mgr); #ifdef HAVE_ALSA LASH_PRINT_DEBUG("destroying alsa manager"); alsa_mgr_lock(server->alsa_mgr); alsa_mgr_destroy(server->alsa_mgr); #endif LASH_PRINT_DEBUG("destroying jack manager"); jack_mgr_lock(server->jack_mgr); jack_mgr_destroy(server->jack_mgr); LASH_PRINT_DEBUG("destroying loader"); signal(SIGCHLD, SIG_IGN); loader_destroy(server->loader); free(server->default_dir); free(server); LASH_PRINT_DEBUG("server destroyed"); } void server_create_loader(server_t * server) { loader_t *loader; if (server->loader) { loader_destroy(server->loader); server->loader = NULL; server->loader_quit = 0; } loader = loader_new(); if (!loader) { fprintf(stderr, "%s: could not create loader; aborting\n", __FUNCTION__); abort(); } loader_fork(loader); server_set_loader(server, loader); } void server_set_loader(server_t * server, loader_t * loader) { server->loader = loader; } conn_mgr_t * server_get_conn_mgr(server_t * server) { return server->conn_mgr; } const char * server_get_default_dir(server_t * server) { return server->default_dir; } int server_get_loader_quit(server_t * server) { return server->loader_quit; } void server_set_loader_quit(server_t * server, int quit) { server->loader_quit = quit; } void server_send_event(server_t * server, server_event_t * server_event) { LASH_DEBUGARGS("sending server event with type %d", server_event->type); pthread_mutex_lock(&server->server_events_lock); server->server_events = lash_list_append(server->server_events, server_event); pthread_mutex_unlock(&server->server_events_lock); pthread_cond_signal(&server->server_event_cond); } /***************************** ******* server stuff ******** *****************************/ project_t * server_find_project_by_name(server_t * server, const char *project_name) { lash_list_t *node; project_t *project; if (!project_name) return NULL; for (node = server->projects; node; node = lash_list_next(node)) { project = (project_t *) node->data; if (strcmp(project->name, project_name) == 0) return project; } return NULL; } project_t * server_find_project_by_conn_id(server_t * server, unsigned long conn_id) { lash_list_t *list, *client_list; project_t *project; client_t *client; list = server->projects; while (list) { project = (project_t *) list->data; client_list = project->clients; while (client_list) { client = (client_t *) client_list->data; if (client->conn_id == conn_id) return project; client_list = client_list->next; } list = list->next; } return NULL; } int find_client_in_list_by_conn_id(lash_list_t * client_list, unsigned long conn_id, client_t ** client_ptr) { client_t *client; while (client_list) { client = (client_t *) client_list->data; if (client->conn_id == conn_id) { if (client_ptr) *client_ptr = client; return 1; } client_list = client_list->next; } return 0; } void server_find_project_and_client_by_conn_id(server_t * server, unsigned long conn_id, project_t ** project_ptr, client_t ** client_ptr) { lash_list_t *list, *client_list; project_t *project; int found; list = server->projects; while (list) { project = (project_t *) list->data; client_list = project->clients; found = find_client_in_list_by_conn_id(project->clients, conn_id, client_ptr); if (found) { if (project_ptr) *project_ptr = project; return; } list = list->next; } found = find_client_in_list_by_conn_id(server->interfaces, conn_id, client_ptr); if (found) { if (project_ptr) *project_ptr = NULL; return; } if (client_ptr) *client_ptr = NULL; if (project_ptr) *project_ptr = NULL; } client_t * server_find_client_by_conn_id(server_t * server, unsigned long conn_id) { lash_list_t *list; project_t *project; client_t *client; project = server_find_project_by_conn_id(server, conn_id); if (!project) return NULL; list = project->clients; while (list) { client = (client_t *) list->data; if (client->conn_id == conn_id) return client; list = list->next; } return NULL; } void server_notify_interfaces(project_t * project, client_t * client, enum LASH_Event_Type type, const char *str) { lash_list_t *node; lash_event_t *event; client_t *interface; for (node = project->server->interfaces; node; node = lash_list_next(node)) { interface = (client_t *) node->data; event = lash_event_new_with_type(type); lash_event_set_project(event, project->name); if (client) lash_event_set_client_id(event, client->id); lash_event_set_string(event, str); conn_mgr_send_client_lash_event(project->server->conn_mgr, interface->conn_id, event); } } const char * server_create_new_project_name(server_t * server) { static char new_name[16]; int num = 1; do { sprintf(new_name, "project-%d", num); num++; LASH_DEBUGARGS("trying project name '%s', dir '%s'", new_name, lash_get_fqn(lash_get_fqn (getenv("HOME"), server->default_dir), new_name)); } while (project_name_exists(server->projects, new_name) || access(lash_get_fqn (lash_get_fqn(getenv("HOME"), server->default_dir), new_name), F_OK) == 0); return new_name; } project_t * server_create_new_project(server_t * server, const char *name) { project_t *project; LASH_PRINT_DEBUG("creating new project"); project = project_new(server); if (!name || !strlen(name)) { /* create a project name */ LASH_PRINT_DEBUG("creating new name for project"); project_set_name(project, server_create_new_project_name(server)); } else project_set_name(project, name); LASH_DEBUGARGS("new project's name: %s", project->name); project_set_directory(project, lash_get_fqn(lash_get_fqn(getenv("HOME"), server->default_dir), project->name)); lash_create_dir(project->directory); printf("Created project %s in directory %s\n", project->name, project->directory); server->projects = lash_list_append(server->projects, project); server_notify_interfaces(project, NULL, LASH_Project_Add, project->name); server_notify_interfaces(project, NULL, LASH_Project_Dir, project->directory); return project; } static void server_close_project(server_t * server, project_t * project) { server->projects = lash_list_remove(server->projects, project); server_notify_interfaces(project, NULL, LASH_Project_Remove, project->name); project_destroy(project); } void server_add_interface(server_t * server, client_t * interface) { lash_event_t *event; lash_list_t *pnode, *clnode; project_t *project; client_t *client; server->interfaces = lash_list_append(server->interfaces, interface); /* tell the interface about all of our projects and clients */ for (pnode = server->projects; pnode; pnode = lash_list_next(pnode)) { project = (project_t *) pnode->data; event = lash_event_new_with_type(LASH_Project_Add); lash_event_set_string(event, project->name); conn_mgr_send_client_lash_event(server->conn_mgr, interface->conn_id, event); event = lash_event_new_with_type(LASH_Project_Dir); lash_event_set_project(event, project->name); lash_event_set_string(event, project->directory); conn_mgr_send_client_lash_event(server->conn_mgr, interface->conn_id, event); for (clnode = project->clients; clnode; clnode = lash_list_next(clnode)) { client = (client_t *) clnode->data; event = lash_event_new_with_type(LASH_Client_Add); lash_event_set_project(event, project->name); lash_event_set_client_id(event, client->id); conn_mgr_send_client_lash_event(server->conn_mgr, interface->conn_id, event); if (client->name) { LASH_DEBUGARGS("sending client's name to interface: %s", client->name); event = lash_event_new_with_type(LASH_Client_Name); lash_event_set_project(event, project->name); lash_event_set_client_id(event, client->id); lash_event_set_string(event, client->name); conn_mgr_send_client_lash_event(server->conn_mgr, interface->conn_id, event); } if (client->jack_client_name) { LASH_DEBUGARGS ("sending client's jack client name to interface: %s", client->jack_client_name); event = lash_event_new_with_type(LASH_Jack_Client_Name); lash_event_set_project(event, project->name); lash_event_set_client_id(event, client->id); lash_event_set_string(event, client->jack_client_name); conn_mgr_send_client_lash_event(server->conn_mgr, interface->conn_id, event); } if (client->alsa_client_id) { char id[2]; event = lash_event_new_with_type(LASH_Alsa_Client_ID); lash_event_set_project(event, project->name); lash_event_set_client_id(event, client->id); lash_str_set_alsa_client_id(id, client->alsa_client_id); lash_event_set_string(event, id); LASH_DEBUGARGS ("sending client's alsa client id to interface: %u", client->alsa_client_id); conn_mgr_send_client_lash_event(server->conn_mgr, interface->conn_id, event); } } } } void server_add_client(server_t * server, client_t * client) { project_t *project; LASH_PRINT_DEBUG("adding client"); if (CLIENT_SERVER_INTERFACE(client)) { LASH_PRINT_DEBUG("client is an interface; adding to list"); server_add_interface(server, client); return; } project = server_find_project_by_name(server, client->requested_project); if (!project) { /* try and find an existing project */ if ((!client->requested_project || !strlen(client->requested_project)) && server->projects) { project = (project_t *) server->projects->data; } else { LASH_PRINT_DEBUG("creating new project for client"); project = server_create_new_project(server, client->requested_project); } } LASH_DEBUGARGS("adding client to project '%s'", project->name); project_add_client(project, client); } void server_event_client_connect(server_t * server, server_event_t * event) { client_t *client; client = client_new(); client->conn_id = event->conn_id; client_set_from_connect_params(client, event->data.lash_connect_params); server_add_client(server, client); } void server_event_client_disconnect(server_t * server, server_event_t * event) { client_t *client; project_t *project; lash_list_t *jack_patches = NULL; lash_list_t *alsa_patches = NULL; server_find_project_and_client_by_conn_id(server, event->conn_id, &project, &client); if (!client) { fprintf(stderr, "%s: recieved disconnect for connection id %ld, but no client exists for it :/\n", __FUNCTION__, event->conn_id); return; } if (CLIENT_SERVER_INTERFACE(client)) { server->interfaces = lash_list_remove(server->interfaces, client); client_destroy(client); return; } if (client->jack_client_name) { jack_mgr_lock(server->jack_mgr); jack_patches = jack_mgr_remove_client(server->jack_mgr, client->id); jack_mgr_unlock(server->jack_mgr); } #ifdef HAVE_ALSA if (client->alsa_client_id) { alsa_mgr_lock(server->alsa_mgr); alsa_patches = alsa_mgr_remove_client(server->alsa_mgr, client->id); alsa_mgr_unlock(server->alsa_mgr); } #endif project_lose_client(project, client, jack_patches, alsa_patches); if (!project->clients) server_close_project(server, project); } void server_event_client_event(server_t * server, server_event_t * server_event) { lash_event_t *event = NULL; project_t *project = NULL; client_t *client = NULL, *ref_client = NULL; event = server_event_take_lash_event(server_event); /* find the project and client */ server_find_project_and_client_by_conn_id(server, server_event->conn_id, &project, &client); if (!client) { fprintf(stderr, "%s: recieved server event from connection manager for unknown client with connection id %ld\n", __FUNCTION__, server_event->conn_id); return; } if (!project) { if (!CLIENT_SERVER_INTERFACE(client)) { fprintf(stderr, "%s: recieved server event from client '%s' " "that isn't connected to a project\n", __FUNCTION__, client_get_id_str(client)); return; } project = server_find_project_by_name(server, lash_event_get_project(event)); if (project && !uuid_is_null(event->client_id)) { ref_client = project_get_client_by_id(project, event->client_id); if (!ref_client) { fprintf(stderr, "%s: recieved event from server interface with non-existant client " "in project '%s'\n", __FUNCTION__, project->name); return; } } } switch (lash_event_get_type(event)) { case LASH_Client_Name: if (CLIENT_SERVER_INTERFACE(client)) server_lash_event_client_name(project, ref_client, client, lash_event_get_string(event)); else server_lash_event_client_name(project, client, client, lash_event_get_string(event)); break; case LASH_Jack_Client_Name: if (CLIENT_SERVER_INTERFACE(client)) server_lash_event_jack_client_name(project, ref_client, client, lash_event_get_string(event)); else server_lash_event_jack_client_name(project, client, client, lash_event_get_string(event)); break; case LASH_Alsa_Client_ID: if (CLIENT_SERVER_INTERFACE(client)) server_lash_event_alsa_client_id(project, ref_client, client, lash_event_get_string(event)); else server_lash_event_alsa_client_id(project, client, client, lash_event_get_string(event)); break; case LASH_Restore_Data_Set: server_lash_event_restore_data_set(project, client); break; case LASH_Save_Data_Set: server_lash_event_save_data_set(project, client); break; case LASH_Restore_File: break; case LASH_Save_File: server_lash_event_save_file(project, client); break; case LASH_Save: project_save(project); break; case LASH_Quit: break; case LASH_Project_Add: server_lash_event_project_add(server, lash_event_get_string(event)); break; case LASH_Project_Remove: server_close_project(server, project); break; case LASH_Project_Dir: server_lash_event_project_dir(project, client, lash_event_get_string(event)); break; case LASH_Project_Name: server_lash_event_project_name(project, lash_event_get_string(event)); break; case LASH_Client_Remove: server_lash_event_client_remove(project, ref_client); break; /* case LASH_Percentage: */ default: fprintf(stderr, "%s: received unknown event of type %d\n", __FUNCTION__, lash_event_get_type(event)); break; } lash_event_destroy(event); } void server_event_client_config(server_t * server, server_event_t * event) { client_t *client; store_t *store; /* find the project and client */ server_find_project_and_client_by_conn_id(server, event->conn_id, NULL, &client); if (!client) { fprintf(stderr, "%s: recieved server event from connection manager for unknown client with connection id %ld\n", __FUNCTION__, event->conn_id); return; } if (!CLIENT_CONFIG_DATA_SET(client)) { fprintf(stderr, "%s: client '%s' sent us a config but doesn't describe itself as configured with a data set\n", __FUNCTION__, client_get_id_str(client)); return; } store = client->store; store_set_config(store, event->data.lash_config); } void server_deal_with_event(server_t * server, server_event_t * event) { switch (event->type) { case Client_Connect: server_event_client_connect(server, event); break; case Client_Disconnect: server_event_client_disconnect(server, event); break; case Client_Event: server_event_client_event(server, event); break; case Client_Config: server_event_client_config(server, event); break; default: fprintf(stderr, "%s: recieved unknown server event of type %d\n", __FUNCTION__, event->type); } } void server_main(server_t * server) { server_event_t *server_event; lash_list_t *server_events, *list; struct timespec ts; struct timeval now; while (!server->quit) { pthread_mutex_lock(&server->server_events_lock); server_events = server->server_events; if (!server_events) { gettimeofday(&now, NULL); ts.tv_sec = now.tv_sec + 1; ts.tv_nsec = now.tv_usec * 1000; pthread_cond_timedwait(&server->server_event_cond, &server->server_events_lock, &ts); server_events = server->server_events; } server->server_events = NULL; pthread_mutex_unlock(&server->server_events_lock); for (list = server_events; list; list = lash_list_next(list)) { server_event = (server_event_t *) list->data; LASH_DEBUGARGS("recieved event of type %d", server_event->type); server_deal_with_event(server, server_event); server_event_destroy(server_event); } lash_list_free(server_events); } LASH_PRINT_DEBUG("finished"); } /* EOF */