2 /*******************************************************************************/
3 /* Copyright (C) 2010 Jonathan Moore Liles */
5 /* This program is free software; you can redistribute it and/or modify it */
6 /* under the terms of the GNU General Public License as published by the */
7 /* Free Software Foundation; either version 2 of the License, or (at your */
8 /* option) any later version. */
10 /* This program is distributed in the hope that it will be useful, but WITHOUT */
11 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
12 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */
15 /* You should have received a copy of the GNU General Public License along */
16 /* with This program; see the file COPYING. If not,write to the Free Software */
17 /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /*******************************************************************************/
20 #define __MODULE__ "nsmd"
34 #include <sys/types.h>
36 #include <sys/signalfd.h>
48 #include <OSC/Endpoint.H>
52 #pragma GCC diagnostic ignored "-Wunused-parameter"
54 static OSC::Endpoint *osc_server;
55 static lo_address gui_addr;
56 static bool gui_is_active = false;
59 static int session_lock_fd = 0;
60 static char *session_root;
62 #define NSM_API_VERSION_MAJOR 1
63 #define NSM_API_VERSION_MINOR 0
66 #define ERR_GENERAL_ERROR -1
67 #define ERR_INCOMPATIBLE_API -2
68 #define ERR_BLACKLISTED -3
69 #define ERR_LAUNCH_FAILED -4
70 #define ERR_NO_SUCH_FILE -5
71 #define ERR_NO_SESSION_OPEN -6
72 #define ERR_UNSAVED_CHANGES -7
73 #define ERR_NOT_NOW -8
74 #define ERR_BAD_PROJECT -9
75 #define ERR_CREATE_FAILED -10
76 #define ERR_SESSION_LOCKED -11
78 #define APP_TITLE "Non Session Manager"
96 int _pending_command; /* */
97 struct timeval _command_sent_time;
105 lo_address addr; /* */
106 char *name; /* client application name */
107 char *executable_path; /* path to client executable */
108 int pid; /* PID of client process */
109 float progress; /* */
110 bool active; /* client has registered via announce */
111 // bool stopped; /* the client quit, but not because we told it to--user still has to decide to remove it from the session */
112 char *client_id; /* short part of client ID */
113 char *capabilities; /* client capabilities... will be null for dumb clients */
114 bool dirty; /* flag for client self-reported dirtiness */
118 const char *label ( void ) const { return _label; }
119 void label ( const char *l )
123 _label = strdup( l );
126 bool gui_visible ( void ) const
131 void gui_visible ( bool b )
137 has_error ( void ) const
139 return _reply_errcode != 0;
143 error_code ( void ) const
145 return _reply_errcode;
148 const char * message ( void )
150 return _reply_message;
154 set_reply ( int errcode, const char *message )
156 if ( _reply_message )
157 free( _reply_message );
159 _reply_message = strdup( message );
160 _reply_errcode = errcode;
163 bool reply_pending ( void )
165 return _pending_command != COMMAND_NONE;
168 bool is_dumb_client ( void )
170 return capabilities == NULL;
173 void pending_command ( int command )
175 gettimeofday( &_command_sent_time, NULL );
176 _pending_command = command;
179 double milliseconds_since_last_command ( void ) const
183 gettimeofday( &now, NULL );
185 double elapsedms = ( now.tv_sec - _command_sent_time.tv_sec ) * 1000.0;
186 elapsedms += ( now.tv_usec - _command_sent_time.tv_usec ) / 1000.0;
191 int pending_command ( void )
193 return _pending_command;
196 // capability should be enclosed in colons. I.e. ":switch:"
198 is_capable_of ( const char *capability ) const
200 return capabilities &&
201 strstr( capabilities, capability );
212 _pending_command = 0;
218 pre_existing = false;
226 free(executable_path);
232 name = executable_path = client_id = capabilities = NULL;
237 static std::list< Client* > client;
239 /* helper macros for defining OSC handlers */
240 #define OSC_NAME( name ) osc_ ## name
241 // #define OSCDMSG() DMESSAGE( "Got OSC message: %s", path );
242 #define OSC_HANDLER( name ) static int OSC_NAME( name ) ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
244 static char *session_path = NULL;
245 static char *session_name = NULL;
248 clients_have_errors ( )
250 for ( std::list<Client*>::const_iterator i = client.begin();
253 if ( (*i)->active && (*i)->has_error() )
260 get_client_by_pid ( int pid )
262 std::list<Client*> *cl = &client;
264 for ( std::list<Client*>::const_iterator i = cl->begin();
267 if ( (*i)->pid == pid )
273 void clear_clients ( void )
275 std::list<Client*> *cl = &client;
277 for ( std::list<Client*>::iterator i = cl->begin();
288 handle_client_process_death ( int pid )
290 Client *c = get_client_by_pid( (int)pid );
294 MESSAGE( "Client %s died.", c->name );
296 bool dead_because_we_said = false;
298 if ( c->pending_command() == COMMAND_KILL ||
299 c->pending_command() == COMMAND_QUIT )
301 dead_because_we_said = true;
304 c->pending_command( COMMAND_NONE );
309 if ( dead_because_we_said )
312 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "removed" );
321 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "stopped" );
327 void handle_sigchld ( )
332 pid_t pid = waitpid(-1, &status, WNOHANG);
337 handle_client_process_death( pid );
343 path_is_valid ( const char *path )
347 asprintf( &s, "/%s/", path );
349 int r = strstr( s, "/../" ) == NULL;
358 mkpath ( const char *path, bool create_final_directory )
360 char *p = strdup( path );
364 while ( ( i = index( i, '/' ) ) )
370 if ( stat( p, &st ) )
372 if ( mkdir( p, 0711 ) )
383 if ( create_final_directory )
385 if ( mkdir( p, 0711 ) )
398 set_name ( const char *name )
401 free( session_name );
403 char *s = strdup( name );
405 session_name = strdup( basename( s ) );
411 address_matches ( lo_address addr1, lo_address addr2 )
413 /* char *url1 = lo_address_get_url( addr1 ); */
414 /* char *url2 = lo_address_get_url( addr2 ); */
416 char *url1 = strdup( lo_address_get_port( addr1 ) );
417 char *url2 = strdup(lo_address_get_port( addr2 ) );
419 bool r = !strcmp( url1, url2 );
428 get_client_by_id ( std::list<Client*> *cl, const char *id )
430 for ( std::list<Client*>::const_iterator i = cl->begin();
433 if ( !strcmp( (*i)->client_id, id ) )
441 get_client_by_name_and_id ( std::list<Client*> *cl, const char *name, const char *id )
443 for ( std::list<Client*>::const_iterator i = cl->begin();
446 if ( !strcmp( (*i)->client_id, id ) &&
447 ! strcmp( (*i)->name, name ) )
454 get_client_by_address ( lo_address addr )
456 for ( std::list<Client*>::iterator i = client.begin();
459 if ( (*i)->addr && address_matches( (*i)->addr, addr ) )
466 replies_still_pending ( )
468 for ( std::list<Client*>::const_iterator i = client.begin();
471 /* if ( (*i)->active && (*i)->reply_pending() ) */
473 if ( (*i)->reply_pending() )
480 generate_client_id ( Client *c )
487 for ( int i = 1; i < 5; i++)
488 id_str[i] = 'A' + (rand() % 25);
490 return strdup(id_str);
496 fprintf( stdout, "Waiting..." );
506 osc_server->wait( 1000 );
508 if ( ! replies_still_pending() )
512 /* FIXME: do something about unresponsive clients */
517 get_client_project_path ( const char *session_path, Client *c )
519 char *client_project_path;
521 asprintf( &client_project_path, "%s/%s.%s", session_path, c->name, c->client_id );
523 return client_project_path;
527 launch ( const char *executable, const char *client_id )
531 if ( !client_id || !( c = get_client_by_id( &client, client_id ) ) )
535 c->executable_path = strdup( executable );
538 char *s = strdup( c->executable_path );
540 c->name = strdup( basename( s ) );
546 c->client_id = strdup( client_id );
548 c->client_id = generate_client_id( c );
550 client.push_back( c );
553 char * url = osc_server->url();
556 if ( ! (pid = fork()) )
558 MESSAGE( "Launching %s\n", executable );
560 char *args[] = { strdup( executable ), NULL };
562 setenv( "NSM_URL", url, 1 );
564 if ( -1 == execvp( executable, args ) )
566 WARNING( "Error starting process: %s", strerror( errno ) );
572 c->pending_command( COMMAND_START );
575 MESSAGE( "Process has pid: %i", pid );
579 osc_server->send( gui_addr, "/nsm/gui/client/new", c->client_id, c->name );
580 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "launch" );
587 command_client_to_save ( Client *c )
591 MESSAGE( "Telling %s to save", c->name );
592 osc_server->send( c->addr, "/nsm/client/save" );
594 c->pending_command( COMMAND_SAVE );
597 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "save" );
599 else if ( c->is_dumb_client() )
601 // this is a dumb client...
603 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "noop" );
607 void command_client_to_switch ( Client *c, const char *new_client_id )
609 char *old_client_id = c->client_id;
611 c->client_id = strdup( new_client_id );
613 char *client_project_path = get_client_project_path( session_path, c );
615 MESSAGE( "Commanding %s to switch \"%s\"", c->name, client_project_path );
617 char *full_client_id;
618 asprintf( &full_client_id, "%s.%s", c->name, c->client_id );
620 osc_server->send( c->addr, "/nsm/client/open", client_project_path, session_name, full_client_id );
622 free( full_client_id );
623 free( client_project_path );
625 c->pending_command( COMMAND_OPEN );
629 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "switch" );
630 osc_server->send( gui_addr, "/nsm/gui/client/switch", old_client_id, c->client_id );
633 free( old_client_id );
637 purge_inactive_clients ( )
639 for ( std::list<Client*>::iterator i = client.begin();
643 if ( ! (*i)->active )
646 osc_server->send( gui_addr, "/nsm/gui/client/status", (*i)->client_id, (*i)->status = "removed" );
650 i = client.erase( i );
656 process_is_running ( int pid )
658 if ( 0 == kill( pid, 0 ) )
662 else if ( ESRCH == errno )
671 purge_dead_clients ( )
673 std::list<Client*> tmp( client );
675 for ( std::list<Client*>::const_iterator i = tmp.begin();
679 const Client *c = *i;
682 if ( ! process_is_running( c->pid ) )
683 handle_client_process_death( c->pid );
688 /************************/
689 /* OSC Message Handlers */
690 /************************/
694 if ( ! session_path )
696 osc_server->send( lo_message_get_source( msg ), path,
698 "Cannot add to session because no session is loaded." );
704 if ( ! launch( &argv[0]->s, NULL ) )
706 osc_server->send( lo_message_get_source( msg ), path,
708 "Failed to launch process!" );
712 osc_server->send( lo_message_get_source( msg ), path,
720 OSC_HANDLER( announce )
722 MESSAGE( "Got announce" );
724 const char *client_name = &argv[0]->s;
725 const char *capabilities = &argv[1]->s;
726 const char *executable_path = &argv[2]->s;
727 int major = argv[3]->i;
728 int minor = argv[4]->i;
729 int pid = argv[5]->i;
731 if ( ! session_path )
733 osc_server->send( lo_message_get_source( msg ), "/error",
736 "Sorry, but there's no session open for this application to join." );
740 bool expected_client = false;
744 for ( std::list<Client*>::iterator i = client.begin();
748 if ( ! strcmp( (*i)->executable_path, executable_path ) &&
750 (*i)->pending_command() == COMMAND_START )
752 // I think we've found the slot we were looking for.
753 MESSAGE( "Client was expected." );
762 c->executable_path = strdup( executable_path );
763 c->client_id = generate_client_id( c );
766 expected_client = true;
768 if ( major > NSM_API_VERSION_MAJOR )
770 DMESSAGE( "Client is using incompatible and more recent API version %i.%i", major, minor );
772 osc_server->send( lo_message_get_source( msg ), "/error",
774 ERR_INCOMPATIBLE_API,
775 "Server is using an incompatible API version." );
781 c->capabilities = strdup( capabilities );
782 c->addr = lo_address_new_from_url( lo_address_get_url( lo_message_get_source( msg ) ));
783 c->name = strdup( client_name );
786 MESSAGE( "Process has pid: %i", pid );
788 if ( ! expected_client )
789 client.push_back( c );
791 MESSAGE( "The client \"%s\" at \"%s\" informs us it's ready to receive commands.", &argv[0]->s, lo_address_get_url( c->addr ) );
793 osc_server->send( lo_message_get_source( msg ), "/reply",
796 "Howdy, what took you so long?" :
797 "Well hello, stranger. Welcome to the party.",
799 ":server-control:broadcast:" );
803 osc_server->send( gui_addr, "/nsm/gui/client/new", c->client_id, c->name );
804 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "open" );
806 if ( c->is_capable_of( ":optional-gui:" ) )
807 osc_server->send( gui_addr, "/nsm/gui/client/has_optional_gui", c->client_id );
811 char *full_client_id;
812 asprintf( &full_client_id, "%s.%s", c->name, c->client_id );
814 char *client_project_path = get_client_project_path( session_path, c );
816 osc_server->send( lo_message_get_source( msg ), "/nsm/client/open", client_project_path, session_name, full_client_id );
818 c->pending_command( COMMAND_OPEN );
820 free( full_client_id );
821 free( client_project_path );
828 save_session_file ( )
830 char *session_file = NULL;
831 asprintf( &session_file, "%s/session.nsm", session_path );
833 FILE *fp = fopen( session_file, "w+" );
835 /* FIXME: handle errors. */
837 for ( std::list<Client*>::iterator i = client.begin();
841 fprintf( fp, "%s:%s:%s\n", (*i)->name, (*i)->executable_path, (*i)->client_id );
848 client_by_name ( const char *name,
849 std::list<Client*> *cl )
851 for ( std::list<Client*>::iterator i = cl->begin();
855 if ( !strcmp( name, (*i)->name ) )
864 dumb_clients_are_alive ( )
866 std::list<Client*> *cl = &client;
868 for ( std::list<Client*>::iterator i = cl->begin();
872 if ( (*i)->is_dumb_client() && (*i)->pid > 0 )
880 wait_for_dumb_clients_to_die ( )
882 struct signalfd_siginfo fdsi;
884 MESSAGE( "Waiting for any dumb clients to die." );
886 for ( int i = 0; i < 6; i++ )
888 MESSAGE( "Loop %i", i );
890 if ( ! dumb_clients_are_alive() )
893 ssize_t s = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo));
895 if (s == sizeof(struct signalfd_siginfo))
897 if (fdsi.ssi_signo == SIGCHLD)
904 /* FIXME: give up on remaining clients and purge them */
909 killed_clients_are_alive ( )
911 std::list<Client*> *cl = &client;
913 for ( std::list<Client*>::iterator i = cl->begin();
917 if ( ( (*i)->pending_command() == COMMAND_QUIT ||
918 (*i)->pending_command() == COMMAND_KILL ) &&
927 wait_for_killed_clients_to_die ( )
929 struct signalfd_siginfo fdsi;
931 MESSAGE( "Waiting for killed clients to die." );
933 for ( int i = 0; i < 24; i++ )
935 MESSAGE( "Loop %i", i );
937 if ( ! killed_clients_are_alive() )
940 ssize_t s = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo));
942 if (s == sizeof(struct signalfd_siginfo))
944 if (fdsi.ssi_signo == SIGCHLD)
948 purge_dead_clients();
950 usleep( 200 * 1000 );
953 if ( killed_clients_are_alive() )
955 WARNING( "Killed clients are still alive" );
956 /* FIXME: give up on remaining clients and purge them */
959 MESSAGE( "All clients have died." );
965 command_all_clients_to_save ( )
969 MESSAGE( "Commanding attached clients to save." );
971 for ( std::list<Client*>::iterator i = client.begin();
975 command_client_to_save( *i );
986 command_client_to_quit ( Client *c )
988 MESSAGE( "Commanding %s to quit", c->name );
992 c->pending_command( COMMAND_QUIT );
994 kill( c->pid, SIGTERM );
997 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "quit" );
999 else if ( c->is_dumb_client() )
1003 if ( gui_is_active )
1004 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "kill" );
1006 /* should be kill? */
1007 c->pending_command( COMMAND_KILL );
1009 // this is a dumb client... try and kill it
1010 kill( c->pid, SIGTERM );
1014 if ( gui_is_active )
1015 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "removed" );
1023 if ( ! session_path )
1026 for ( std::list<Client*>::iterator i = client.begin();
1030 command_client_to_quit( *i );
1033 wait_for_killed_clients_to_die();
1035 purge_inactive_clients();
1042 asprintf( &session_lock, "%s/.lock", session_path );
1044 release_lock( &session_lock_fd, session_lock );
1049 session_path = NULL;
1051 session_name = NULL;
1054 if ( gui_is_active )
1056 osc_server->send( gui_addr, "/nsm/gui/session/name", "" );
1061 tell_client_session_is_loaded( Client *c )
1064 //!c->is_dumb_client() )
1066 MESSAGE( "Telling client %s that session is loaded.", c->name );
1067 osc_server->send( c->addr, "/nsm/client/session_is_loaded" );
1072 tell_all_clients_session_is_loaded ( void )
1074 MESSAGE( "Telling all clients that session is loaded..." );
1076 for ( std::list<Client*>::iterator i = client.begin();
1080 tell_client_session_is_loaded( *i );
1085 load_session_file ( const char * path )
1087 char *session_file = NULL;
1088 asprintf( &session_file, "%s/session.nsm", path );
1089 char *session_lock = NULL;
1090 asprintf( &session_lock, "%s/.lock", path );
1092 if ( ! acquire_lock( &session_lock_fd, session_lock ) )
1094 free( session_file );
1095 free( session_lock );
1097 WARNING( "Session is locked by another process" );
1098 return ERR_SESSION_LOCKED;
1104 if ( ! ( fp = fopen( session_file, "r" ) ) )
1106 free( session_file );
1107 return ERR_CREATE_FAILED;
1110 session_path = strdup( path );
1114 std::list<Client*> new_clients;
1117 char * client_name = NULL;
1118 char * client_executable = NULL;
1119 char * client_id = NULL;
1121 // load the client list
1122 while ( fscanf( fp, "%a[^:]:%a[^:]:%a[^:\n]\n", &client_name, &client_executable, &client_id ) > 0 )
1124 Client *c = new Client();
1126 c->name = client_name;
1127 c->executable_path = client_executable;
1128 c->client_id = client_id;
1130 new_clients.push_back( c );
1134 MESSAGE( "Commanding unneeded and dumb clients to quit" );
1136 for ( std::list<Client*>::iterator i = client.begin();
1140 if ( ! (*i)->is_capable_of( ":switch:" )
1142 ! client_by_name( (*i)->name, &new_clients ) )
1144 command_client_to_quit( *i );
1148 // wait_for_replies();
1150 wait_for_killed_clients_to_die();
1152 // wait_for_dumb_clients_to_die();
1154 purge_inactive_clients();
1156 for ( std::list<Client*>::iterator i = client.begin();
1160 (*i)->pre_existing = true;
1163 MESSAGE( "Commanding smart clients to switch" );
1165 for ( std::list<Client*>::iterator i = new_clients.begin();
1166 i != new_clients.end();
1171 /* in a duplicated session, clients will have the same
1172 * IDs, so be sure to pick the right one to avoid race
1173 * conditions in JACK name registration. */
1174 c = get_client_by_name_and_id( &client, (*i)->name, (*i)->client_id );
1177 c = client_by_name( (*i)->name, &client );
1179 if ( c && c->pre_existing && !c->reply_pending() )
1181 // since we already shutdown clients not capable of 'switch', we can assume that these are.
1182 command_client_to_switch( c, (*i)->client_id );
1186 /* sleep a little bit because liblo derives its sequence
1187 * of port numbers from the system time (second
1188 * resolution) and if too many clients start at once they
1189 * won't be able to find a free port. */
1190 usleep( 100 * 1000 );
1192 launch( (*i)->executable_path, (*i)->client_id );
1196 /* this part is a little tricky... the clients need some time to
1197 * send their 'announce' messages before we can send them 'open'
1198 * and know that a reply is pending and we should continue waiting
1199 * until they finish. wait_for_replies() must check for OSC
1200 * messages immediately, even if no replies seem to be pending
1203 // osc_server->wait( 3000 );
1207 tell_all_clients_session_is_loaded();
1209 MESSAGE( "Loaded." );
1211 new_clients.clear();
1213 if ( gui_is_active )
1215 osc_server->send( gui_addr, "/nsm/gui/session/name", session_name );
1223 if ( ! session_path )
1225 osc_server->send( lo_message_get_source( msg ), "/error", path,
1226 ERR_NO_SESSION_OPEN,
1227 "No session to save.");
1232 command_all_clients_to_save();
1236 osc_server->send( lo_message_get_source( msg ), "/reply", path, "Saved." );
1241 OSC_HANDLER( duplicate )
1243 if ( ! session_path )
1245 osc_server->send( lo_message_get_source( msg ), "/error", path,
1246 ERR_NO_SESSION_OPEN,
1247 "No session to duplicate.");
1252 if ( ! path_is_valid( &argv[0]->s ) )
1254 osc_server->send( lo_message_get_source( msg ), "/error", path,
1256 "Invalid session name." );
1261 command_all_clients_to_save();
1263 if ( clients_have_errors() )
1265 osc_server->send( lo_message_get_source( msg ), "/error", path,
1267 "Some clients could not save" );
1272 // save_session_file();
1275 asprintf( &spath, "%s/%s", session_root, &argv[0]->s );
1277 mkpath( spath, false );
1279 /* FIXME: code a recursive copy instead of calling the shell */
1282 asprintf( &cmd, "cp -R \"%s\" \"%s\"", session_path, spath);
1288 osc_server->send( gui_addr, "/nsm/gui/session/session", &argv[0]->s );
1290 MESSAGE( "Attempting to open %s", spath );
1292 if ( load_session_file( spath ) )
1294 MESSAGE( "Loaded" );
1295 osc_server->send( lo_message_get_source( msg ), "/reply", path,
1300 MESSAGE( "Failed" );
1301 osc_server->send( lo_message_get_source( msg ), "/error", path,
1311 osc_server->send( lo_message_get_source( msg ), "/reply", path, "Duplicated." );
1318 if ( ! path_is_valid( &argv[0]->s ) )
1320 osc_server->send( lo_message_get_source( msg ), "/error", path,
1322 "Invalid session name." );
1329 command_all_clients_to_save();
1334 MESSAGE( "Creating new session" );
1338 asprintf( &spath, "%s/%s", session_root, &argv[0]->s );
1340 if ( mkpath( spath, true ) )
1342 osc_server->send( lo_message_get_source( msg ), "/error", path,
1344 "Could not create the session directory" );
1351 session_path = strdup( spath );
1353 set_name( session_path );
1355 osc_server->send( lo_message_get_source( msg ), "/reply", path, "Created." );
1357 if ( gui_is_active )
1359 osc_server->send( gui_addr, "/nsm/gui/session/session", &argv[0]->s );
1360 osc_server->send( gui_addr, "/nsm/gui/session/name", &argv[0]->s );
1363 save_session_file();
1367 osc_server->send( lo_message_get_source( msg ), "/reply", path,
1368 "Session created" );
1373 static lo_address list_response_address;
1376 list_file ( const char *fpath, const struct stat *sb, int tflag )
1380 if ( tflag == FTW_F )
1382 s = strdup( fpath );
1383 if ( ! strcmp( "session.nsm", basename( s ) ) )
1386 s = strdup( fpath );
1390 memmove( s, s + strlen( session_root ) + 1, (strlen( s ) - strlen( session_root )) + 1);
1392 osc_server->send( list_response_address, "/reply", "/nsm/server/list", s );
1405 MESSAGE( "Listing sessions" );
1407 list_response_address = lo_message_get_source( msg );
1409 ftw( session_root, list_file, 20 );
1411 osc_server->send( lo_message_get_source( msg ), path,
1420 DMESSAGE( "Got open" );
1424 command_all_clients_to_save();
1426 if ( clients_have_errors() )
1428 osc_server->send( lo_message_get_source( msg ), "/error", path,
1430 "Some clients could not save" );
1434 // save_session_file();
1438 asprintf( &spath, "%s/%s", session_root, &argv[0]->s );
1440 MESSAGE( "Attempting to open %s", spath );
1442 int err = load_session_file( spath );
1446 MESSAGE( "Loaded" );
1447 osc_server->send( lo_message_get_source( msg ), "/reply", path,
1452 MESSAGE( "Failed" );
1453 const char *m = NULL;
1456 case ERR_CREATE_FAILED:
1457 m = "Could not create session file!";
1459 case ERR_SESSION_LOCKED:
1460 m = "Session is locked by another process!";
1462 case ERR_NO_SUCH_FILE:
1463 m = "The named session does not exist.";
1466 m = "Unknown error";
1470 osc_server->send( lo_message_get_source( msg ), "/error", path,
1492 OSC_HANDLER( abort )
1494 if ( ! session_path )
1496 osc_server->send( lo_message_get_source( msg ), "/error", path,
1497 ERR_NO_SESSION_OPEN,
1498 "No session to abort." );
1503 MESSAGE( "Commanding attached clients to quit." );
1507 osc_server->send( lo_message_get_source( msg ), "/reply", path,
1515 OSC_HANDLER( close )
1517 if ( ! session_path )
1519 osc_server->send( lo_message_get_source( msg ), "/error", path,
1520 ERR_NO_SESSION_OPEN,
1521 "No session to close." );
1526 command_all_clients_to_save();
1528 MESSAGE( "Commanding attached clients to quit." );
1532 osc_server->send( lo_message_get_source( msg ), "/reply", path,
1541 OSC_HANDLER( broadcast )
1543 const char *to_path = &argv[0]->s;
1545 /* don't allow clients to broadcast NSM commands */
1546 if ( ! strncmp( to_path, "/nsm/", strlen( "/nsm/" ) ) )
1549 std::list<OSC::OSC_Value> new_args;
1551 for ( int i = 1; i < argc; ++i )
1556 new_args.push_back( OSC::OSC_String( &argv[i]->s ) );
1559 new_args.push_back( OSC::OSC_Int( argv[i]->i ) );
1562 new_args.push_back( OSC::OSC_Float( argv[i]->f ) );
1567 char *sender_url = lo_address_get_url( lo_message_get_source( msg ) );
1569 for ( std::list<Client*>::iterator i = client.begin();
1576 char *url = lo_address_get_url( (*i)->addr );
1578 if ( strcmp( sender_url, url ) )
1580 osc_server->send( (*i)->addr, to_path, new_args );
1586 /* also relay to attached GUI so that the broadcast can be
1587 * propagated to another NSMD instance */
1588 if ( gui_is_active )
1591 char *u1 = lo_address_get_url( gui_addr );
1593 if ( strcmp( u1, sender_url ) )
1595 new_args.push_front( OSC::OSC_String( to_path ) );
1597 osc_server->send( gui_addr, path, new_args );
1610 /*********************************/
1611 /* Client Informational Messages */
1612 /*********************************/
1614 OSC_HANDLER( progress )
1616 Client *c = get_client_by_address( lo_message_get_source( msg ) );
1620 c->progress = argv[0]->f;
1622 MESSAGE( "%s progress: %i%%", c->name, (int)(c->progress * 100.0f) );
1624 if ( gui_is_active )
1626 osc_server->send( gui_addr, "/nsm/gui/client/progress", c->client_id, (float)c->progress );
1633 OSC_HANDLER( is_dirty )
1635 MESSAGE( "Client sends dirty" );
1637 Client *c = get_client_by_address( lo_message_get_source( msg ) );
1644 if ( gui_is_active )
1645 osc_server->send( gui_addr, "/nsm/gui/client/dirty", c->client_id, c->dirty );
1650 OSC_HANDLER( is_clean )
1652 MESSAGE( "Client sends clean" );
1654 Client *c = get_client_by_address( lo_message_get_source( msg ) );
1661 if ( gui_is_active )
1662 osc_server->send( gui_addr, "/nsm/gui/client/dirty", c->client_id, c->dirty );
1667 OSC_HANDLER( gui_is_hidden )
1669 MESSAGE( "Client sends gui hidden" );
1671 Client *c = get_client_by_address( lo_message_get_source( msg ) );
1676 c->gui_visible( false );
1678 if ( gui_is_active )
1679 osc_server->send( gui_addr, "/nsm/gui/client/gui_visible", c->client_id, c->gui_visible() );
1684 OSC_HANDLER( gui_is_shown )
1686 MESSAGE( "Client sends gui shown" );
1688 Client *c = get_client_by_address( lo_message_get_source( msg ) );
1693 c->gui_visible( true );
1695 if ( gui_is_active )
1696 osc_server->send( gui_addr, "/nsm/gui/client/gui_visible", c->client_id, c->gui_visible() );
1701 OSC_HANDLER( message )
1703 Client *c = get_client_by_address( lo_message_get_source( msg ) );
1708 if ( gui_is_active )
1709 osc_server->send( gui_addr, "/nsm/gui/client/message", c->client_id, argv[0]->i, &argv[1]->s );
1714 OSC_HANDLER( label )
1716 Client *c = get_client_by_address( lo_message_get_source( msg ) );
1721 if ( strcmp( types, "s" ) )
1724 c->label( &argv[0]->s );
1726 if ( gui_is_active )
1727 osc_server->send( gui_addr, "/nsm/gui/client/label", c->client_id, &argv[0]->s );
1732 /**********************/
1733 /* Response Handlers */
1734 /**********************/
1737 OSC_HANDLER( error )
1739 Client *c = get_client_by_address( lo_message_get_source( msg ) );
1743 WARNING( "Error from unknown client" );
1747 // const char *rpath = &argv[0]->s;
1749 int err_code = argv[1]->i;
1751 const char *message = &argv[2]->s;
1753 c->set_reply( err_code, message );
1755 MESSAGE( "Client \"%s\" replied with error: %s (%i) in %fms", c->name, message, err_code, c->milliseconds_since_last_command() );
1756 c->pending_command( COMMAND_NONE );
1758 if ( gui_is_active )
1759 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "error" );
1765 OSC_HANDLER( reply )
1767 Client *c = get_client_by_address( lo_message_get_source( msg ) );
1769 // const char *rpath = &argv[0]->s;
1770 const char *message = &argv[1]->s;
1774 c->set_reply( ERR_OK, message );
1776 MESSAGE( "Client \"%s\" replied with: %s in %fms", c->name, message, c->milliseconds_since_last_command() );
1778 c->pending_command( COMMAND_NONE );
1780 if ( gui_is_active )
1781 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "ready" );
1784 MESSAGE( "Reply from unknown client" );
1791 /******************/
1792 /* GUI operations */
1793 /******************/
1798 Client *c = get_client_by_id( &client, &argv[0]->s );
1804 kill( c->pid, SIGTERM );
1806 if ( gui_is_active )
1807 osc_server->send( gui_addr, "/reply", "Client stopped." );
1812 if ( gui_is_active )
1813 osc_server->send( gui_addr, "/error", -10, "No such client." );
1820 OSC_HANDLER( remove )
1822 Client *c = get_client_by_id( &client, &argv[0]->s );
1829 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "removed" );
1835 if ( gui_is_active )
1836 osc_server->send( gui_addr, "/reply", "Client removed." );
1841 if ( gui_is_active )
1842 osc_server->send( gui_addr, "/error", -10, "No such client." );
1849 OSC_HANDLER( resume )
1851 Client *c = get_client_by_id( &client, &argv[0]->s );
1853 /* FIXME: return error if no such client? */
1859 if ( ! launch( c->executable_path, c->client_id ) )
1870 OSC_HANDLER( client_save )
1872 Client *c = get_client_by_id( &client, &argv[0]->s );
1874 /* FIXME: return error if no such client? */
1879 command_client_to_save( c );
1886 OSC_HANDLER( client_show_optional_gui )
1888 Client *c = get_client_by_id( &client, &argv[0]->s );
1890 /* FIXME: return error if no such client? */
1895 osc_server->send( c->addr, "/nsm/client/show_optional_gui" );
1902 OSC_HANDLER( client_hide_optional_gui )
1904 Client *c = get_client_by_id( &client, &argv[0]->s );
1906 /* FIXME: return error if no such client? */
1911 osc_server->send( c->addr, "/nsm/client/hide_optional_gui" );
1919 announce_gui( const char *url, bool is_reply )
1921 gui_addr = lo_address_new_from_url( url );
1922 gui_is_active = true;
1925 osc_server->send( gui_addr, "/nsm/gui/gui_announce", "hi" );
1927 osc_server->send( gui_addr, "/nsm/gui/server_announce", "hi" );
1929 for ( std::list<Client*>::iterator i = client.begin();
1935 osc_server->send( gui_addr, "/nsm/gui/client/new", c->client_id, c->name );
1936 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status );
1940 osc_server->send( gui_addr, "/nsm/gui/session/name", session_name ? session_name : "" );
1942 DMESSAGE( "Registered with GUI" );
1946 OSC_HANDLER( gui_announce )
1948 announce_gui( lo_address_get_url( lo_message_get_source( msg ) ), true );
1955 osc_server->send( lo_message_get_source( msg ), "/reply", path );
1963 int main(int argc, char *argv[])
1967 sigemptyset( &mask );
1968 sigaddset( &mask, SIGCHLD );
1970 sigprocmask(SIG_BLOCK, &mask, NULL );
1972 signal_fd = signalfd( -1, &mask, SFD_NONBLOCK );
1974 /* generate random seed for client ids */
1979 srand( (unsigned int) seconds );
1982 // char *osc_port = "6666";
1983 char *osc_port = NULL;
1984 const char *gui_url = NULL;
1986 static struct option long_options[] =
1988 { "detach", no_argument, 0, 'd' },
1989 { "session-root", required_argument, 0, 's' },
1990 { "osc-port", required_argument, 0, 'p' },
1991 { "gui-url", required_argument, 0, 'g' },
1992 { "help", no_argument, 0, 'h' },
1996 int option_index = 0;
1999 bool detach = false;
2001 while ( ( c = getopt_long_only( argc, argv, "", long_options, &option_index ) ) != -1 )
2009 session_root = optarg;
2012 DMESSAGE( "Using OSC port %s", optarg );
2016 DMESSAGE( "Going to connect to GUI at: %s", optarg );
2020 printf( "Usage: %s [--osc-port portnum] [--session-root path]\n\n", argv[0] );
2026 if ( !session_root )
2027 asprintf( &session_root, "%s/%s", getenv( "HOME" ), "NSM Sessions" );
2031 if ( stat( session_root, &st ) )
2033 if ( mkdir( session_root, 0771 ) )
2035 FATAL( "Failed to create session directory: %s", strerror( errno ) );
2039 MESSAGE( "Session root is: %s", session_root );
2041 osc_server = new OSC::Endpoint();
2043 if ( osc_server->init( LO_UDP, osc_port ) )
2045 FATAL( "Failed to create OSC server." );
2048 printf( "NSM_URL=%s\n", osc_server->url() );
2052 announce_gui( gui_url, false );
2056 osc_server->add_method( "/nsm/server/announce", "sssiii", OSC_NAME( announce ), NULL, "client_name,capabilities,executable,api_version_major,api_version_minor,client_pid" );
2058 /* response handlers */
2059 osc_server->add_method( "/reply", "ss", OSC_NAME( reply ), NULL, "err_code,msg" );
2060 osc_server->add_method( "/error", "sis", OSC_NAME( error ), NULL, "err_code,msg" );
2062 osc_server->add_method( "/nsm/client/progress", "f", OSC_NAME( progress ), NULL, "progress" );
2063 osc_server->add_method( "/nsm/client/is_dirty", "", OSC_NAME( is_dirty ), NULL, "dirtiness" );
2064 osc_server->add_method( "/nsm/client/is_clean", "", OSC_NAME( is_clean ), NULL, "dirtiness" );
2065 osc_server->add_method( "/nsm/client/message", "is", OSC_NAME( message ), NULL, "message" );
2066 osc_server->add_method( "/nsm/client/gui_is_hidden", "", OSC_NAME( gui_is_hidden ), NULL, "message" );
2067 osc_server->add_method( "/nsm/client/gui_is_shown", "", OSC_NAME( gui_is_shown ), NULL, "message" );
2068 osc_server->add_method( "/nsm/client/label", "s", OSC_NAME( label ), NULL, "message" );
2071 osc_server->add_method( "/nsm/gui/gui_announce", "", OSC_NAME( gui_announce ), NULL, "" );
2072 osc_server->add_method( "/nsm/gui/client/stop", "s", OSC_NAME( stop ), NULL, "client_id" );
2073 osc_server->add_method( "/nsm/gui/client/remove", "s", OSC_NAME( remove ), NULL, "client_id" );
2074 osc_server->add_method( "/nsm/gui/client/resume", "s", OSC_NAME( resume ), NULL, "client_id" );
2075 osc_server->add_method( "/nsm/gui/client/save", "s", OSC_NAME( client_save ), NULL, "client_id" );
2076 osc_server->add_method( "/nsm/gui/client/show_optional_gui", "s", OSC_NAME( client_show_optional_gui ), NULL, "client_id" );
2077 osc_server->add_method( "/nsm/gui/client/hide_optional_gui", "s", OSC_NAME( client_hide_optional_gui ), NULL, "client_id" );
2079 osc_server->add_method( "/osc/ping", "", OSC_NAME( ping ), NULL, "" );
2081 osc_server->add_method( "/nsm/server/broadcast", NULL, OSC_NAME( broadcast ), NULL, "" );
2082 osc_server->add_method( "/nsm/server/duplicate", "s", OSC_NAME( duplicate ), NULL, "" );
2083 osc_server->add_method( "/nsm/server/abort", "", OSC_NAME( abort ), NULL, "" );
2084 osc_server->add_method( "/nsm/server/list", "", OSC_NAME( list ), NULL, "" );
2085 osc_server->add_method( "/nsm/server/add", "s", OSC_NAME( add ), NULL, "commandline" );
2086 osc_server->add_method( "/nsm/server/new", "s", OSC_NAME( new ), NULL, "name" );
2087 osc_server->add_method( "/nsm/server/save", "", OSC_NAME( save ), NULL, "" );
2088 osc_server->add_method( "/nsm/server/open", "s", OSC_NAME( open ), NULL, "name" );
2089 osc_server->add_method( "/nsm/server/close", "", OSC_NAME( close ), NULL, "" );
2090 osc_server->add_method( "/nsm/server/quit", "", OSC_NAME( quit ), NULL, "" );
2095 MESSAGE( "Detaching from console" );
2108 struct signalfd_siginfo fdsi;
2111 /* listen for sigchld signals and process OSC messages forever */
2114 ssize_t s = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo));
2116 if (s == sizeof(struct signalfd_siginfo))
2118 if (fdsi.ssi_signo == SIGCHLD)
2122 osc_server->wait( 1000 );
2124 purge_dead_clients();
2127 // osc_server->run();