NSM: Don't allow 'added' clients to replace stopped clients.
[nondaw.git] / session-manager / src / nsmd.C
blob7ea737632cdf5c8d9b0e1eb00908093bd843e4bf
2 /*******************************************************************************/
3 /* Copyright (C) 2010 Jonathan Moore Liles                                     */
4 /*                                                                             */
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.                                                  */
9 /*                                                                             */
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   */
13 /* more details.                                                               */
14 /*                                                                             */
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" 
22 #include "debug.h"
24 #ifndef _GNU_SOURCE
25 #define _GNU_SOURCE
26 #endif
28 #include <errno.h>
29 #include <string.h>
30 #include <list>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <sys/types.h>
35 #include <signal.h>
36 #include <sys/signalfd.h>
37 #include <sys/stat.h>
38 #include <sys/wait.h>
39 #include <unistd.h>
40 #include <time.h>
41 #include <libgen.h>
42 #include <dirent.h>
43 #include <ftw.h>
44 #include <list>
45 #include <getopt.h>
46 #include <sys/time.h>
48 #include <OSC/Endpoint.H>
49 /* for locking */
50 #include "file.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;
57 static int signal_fd;
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
65 #define ERR_OK 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"
80 enum {
81     COMMAND_NONE = 0,
82     COMMAND_QUIT,
83     COMMAND_KILL,
84     COMMAND_SAVE,
85     COMMAND_OPEN,
86     COMMAND_START
89 struct Client
91 private:
93     int _reply_errcode;
94     char *_reply_message;
96     int _pending_command;                /*  */
97     struct timeval _command_sent_time;
99     bool _gui_visible;
101     char *_label;
103 public:
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 */
115     bool pre_existing;
116     const char *status;
118     const char *label ( void ) const { return _label; }
119     void label ( const char *l )
120         {
121             if ( _label ) 
122                 free( _label );
123             _label = strdup( l ); 
124         }
125         
126     bool gui_visible ( void ) const
127         {
128             return _gui_visible;
129         }
131     void gui_visible ( bool b ) 
132         {
133             _gui_visible = b;
134         }
136     bool
137     has_error ( void ) const
138         {
139             return _reply_errcode != 0;
140         }
142     int
143     error_code ( void ) const
144         {
145             return _reply_errcode;
146         }
148     const char * message ( void )
149         {
150             return _reply_message;
151         }
153     void
154     set_reply ( int errcode, const char *message )
155         {
156             if ( _reply_message )
157                 free( _reply_message );
159             _reply_message = strdup( message );
160             _reply_errcode = errcode;
161         }
163     bool reply_pending ( void )
164         {
165             return _pending_command != COMMAND_NONE;
166         }
168     bool is_dumb_client ( void )
169         {
170             return capabilities == NULL;
171         }
173     void pending_command ( int command )
174         {
175             gettimeofday( &_command_sent_time, NULL );
176             _pending_command = command;
177         }
178     
179     double milliseconds_since_last_command ( void ) const
180         {
181             struct timeval now;
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;
188             return elapsedms;
189         }
191     int pending_command ( void )
192         {
193             return _pending_command;
194         }
196 // capability should be enclosed in colons. I.e. ":switch:"
197     bool 
198     is_capable_of ( const char *capability ) const
199         {
200             return capabilities &&
201                 strstr( capabilities, capability );
202         }
204     Client ( )
205         {
206             _gui_visible = true;
207             addr = 0;
208             _reply_errcode = 0;
209             _reply_message = 0;
210             pid = 0;
211             progress = -0;
212             _pending_command = 0;
213             active = false;
214             client_id = 0;
215             capabilities = 0;
216             name = 0;
217             executable_path = 0;
218             pre_existing = false;
219         }
221     ~Client ( ) 
222         {
223             if ( name )
224                 free(name);
225             if (executable_path)
226                 free(executable_path);
227             if (client_id)
228                 free(client_id);
229             if (capabilities)
230                 free(capabilities);
232             name = executable_path = client_id = capabilities = NULL;
233         }
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;
247 bool
248 clients_have_errors ( )
250     for ( std::list<Client*>::const_iterator i = client.begin();
251           i != client.end();
252           ++i )
253         if ( (*i)->active && (*i)->has_error() )
254             return true;
256     return false;
259 Client *
260 get_client_by_pid ( int pid )
262     std::list<Client*> *cl = &client;
264     for ( std::list<Client*>::const_iterator i = cl->begin();
265           i != cl->end();
266           ++i )
267         if ( (*i)->pid == pid )
268             return *i;
269     
270     return NULL;
273 void clear_clients ( void )
275     std::list<Client*> *cl = &client;
277     for ( std::list<Client*>::iterator i = cl->begin();
278           i != cl->end();
279           ++i )
280     {
281         delete *i;
282         i = cl->erase( i );
283     }
287 void
288 handle_client_process_death ( int pid )
290     Client *c = get_client_by_pid( (int)pid );
291         
292     if ( c )
293     {
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 )
300         {
301             dead_because_we_said = true;
302         }
304         c->pending_command( COMMAND_NONE );
305             
306         c->active = false;
307         c->pid = 0;
309         if ( dead_because_we_said )
310         {
311             if ( gui_is_active )
312                 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "removed" );
314             client.remove( c );
316             delete c;
317         }
318         else
319         {
320             if ( gui_is_active )
321                 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "stopped" );
322         }
323     }
327 void handle_sigchld ( )
329     for ( ;; )
330     {
331         int status;
332         pid_t pid = waitpid(-1, &status, WNOHANG);
334         if (pid <= 0) 
335             break;
336         
337         handle_client_process_death( pid );
338     }
343 path_is_valid ( const char *path )
345     char *s;
347     asprintf( &s, "/%s/", path );
349     int r = strstr( s, "/../" ) == NULL;
351     free( s );
353     return r;
358 mkpath ( const char *path, bool create_final_directory )
360     char *p = strdup( path );
362     char *i = p + 1;
364     while ( ( i = index( i, '/' ) ) )
365     {
366         *i = 0;
368         struct stat st;
370         if ( stat( p, &st ) )
371         {
372             if ( mkdir( p, 0711 ) )
373             {
374                 free( p );
375                 return -1;
376             }
377         }
379         *i = '/';
380         i++;
381     } 
383     if ( create_final_directory )
384     {
385         if ( mkdir( p, 0711 ) )
386         {
387             free( p );
388             return -1;
389         }
390     }
391     
392     free( p );
394     return 0;
397 void
398 set_name ( const char *name )
400     if ( session_name )
401         free( session_name );
403     char *s = strdup( name );
404     
405     session_name = strdup( basename( s ) );
407     free( s );
410 bool
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 );
421     free( url1 );
422     free( url2 );
424     return r;
427 Client *
428 get_client_by_id ( std::list<Client*> *cl, const char *id )
430     for ( std::list<Client*>::const_iterator i = cl->begin();
431           i != cl->end();
432           ++i )
433         if ( !strcmp( (*i)->client_id, id ) )
434             return *i;
436     return NULL;
440 Client *
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();
444           i != cl->end();
445           ++i )
446         if ( !strcmp( (*i)->client_id, id ) && 
447              ! strcmp( (*i)->name, name ) )
448             return *i;
450     return NULL;
453 Client *
454 get_client_by_address ( lo_address addr )
456     for ( std::list<Client*>::iterator i = client.begin();
457           i != client.end();
458           ++i )
459         if ( (*i)->addr && address_matches( (*i)->addr, addr ) )
460             return *i;
462     return NULL;
465 bool
466 replies_still_pending ( )
468     for ( std::list<Client*>::const_iterator i = client.begin();
469           i != client.end();
470           ++i )
471         /* if ( (*i)->active && (*i)->reply_pending() ) */
472         /*     return true; */
473         if ( (*i)->reply_pending() )
474             return true;
476     return false;
479 char *
480 generate_client_id ( Client *c ) 
482     char id_str[6];
484     id_str[0] = 'n';
485     id_str[5] = 0;
487     for ( int i = 1; i < 5; i++)
488         id_str[i] = 'A' + (rand() % 25);
490     return strdup(id_str);
493 void
494 wait_for_replies ( )
496     fprintf( stdout, "Waiting..." );
497     fflush(stdout);
499     int n = 7;
501     while ( n-- )
502     {
503         printf( "." );
504         fflush(stdout);
506         osc_server->wait( 1000 );
508         if ( ! replies_still_pending() )
509             break;
510     }
512     /* FIXME: do something about unresponsive clients */
516 char * 
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;
526 bool
527 launch ( const char *executable, const char *client_id )
529     Client *c;
531     if ( !client_id || !( c = get_client_by_id( &client, client_id ) ) )
532     {
533         c = new Client();
535         c->executable_path = strdup( executable );
537         {
538             char *s = strdup( c->executable_path );
539         
540             c->name = strdup( basename( s ) );
541         
542             free( s );
543         }
544     
545         if ( client_id )
546             c->client_id = strdup( client_id );
547         else
548             c->client_id = generate_client_id( c );
550         client.push_back( c );
551     }
553     char * url = osc_server->url();
555     int pid;
556     if ( ! (pid = fork()) )
557     {
558         MESSAGE( "Launching %s\n", executable );
560         char *args[] = { strdup( executable ), NULL };
562         setenv( "NSM_URL", url, 1 );
563         
564         if ( -1 == execvp( executable, args ) )
565         {
566             WARNING( "Error starting process: %s", strerror( errno ) );
568             exit(-1);
569         }
570     }
572     c->pending_command( COMMAND_START );
573     c->pid = pid;
575     MESSAGE( "Process has pid: %i", pid );
577     if ( gui_is_active )
578     {
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" );
581     }
583     return true;
586 void
587 command_client_to_save ( Client *c )
589     if ( c->active )
590     {
591         MESSAGE( "Telling %s to save", c->name );
592         osc_server->send( c->addr, "/nsm/client/save" );
593         
594         c->pending_command( COMMAND_SAVE );
596         if ( gui_is_active )
597             osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "save" );
598     }
599     else if ( c->is_dumb_client() )
600     {
601         // this is a dumb client... 
602         if ( gui_is_active )
603             osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "noop" );
604     }
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 ); 
614     
615     MESSAGE( "Commanding %s to switch \"%s\"", c->name, client_project_path );
616                 
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 );
621     
622     free( full_client_id );
623     free( client_project_path );
624     
625     c->pending_command( COMMAND_OPEN );
627     if ( gui_is_active )
628     {
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 );
631     }
633     free( old_client_id );
636 void
637 purge_inactive_clients ( )
639     for ( std::list<Client*>::iterator i = client.begin();
640           i != client.end();
641           ++i )
642     {
643         if ( ! (*i)->active )
644         {
645             if ( gui_is_active )
646                 osc_server->send( gui_addr, "/nsm/gui/client/status", (*i)->client_id, (*i)->status = "removed" );
648             delete *i;
650             i = client.erase( i );
651         }
652     }
655 bool
656 process_is_running ( int pid )
658     if ( 0 == kill( pid, 0 ) )
659     {
660         return true;
661     }
662     else if ( ESRCH == errno )
663     {
664         return false;
665     }
667     return false;
670 void
671 purge_dead_clients ( )
673     std::list<Client*> tmp( client );
675     for ( std::list<Client*>::const_iterator i = tmp.begin();
676           i != tmp.end();
677           ++i )
678     {
679         const Client *c = *i;
680         if ( c->pid )
681         {
682             if ( ! process_is_running( c->pid ) )
683                 handle_client_process_death( c->pid );
684         }
685     }
688 /************************/
689 /* OSC Message Handlers */
690 /************************/
692 OSC_HANDLER( add )
694     if ( ! session_path )
695     {
696         osc_server->send( lo_message_get_source( msg ), path,
697                           ERR_NO_SESSION_OPEN,
698                           "Cannot add to session because no session is loaded." );
699                           
701         return 0;
702     }
704     if ( ! launch( &argv[0]->s, NULL ) )
705     {
706         osc_server->send( lo_message_get_source( msg ), path,
707                           ERR_LAUNCH_FAILED,
708                           "Failed to launch process!" );
709     }
710     else
711     {
712         osc_server->send( lo_message_get_source( msg ), path,
713                           ERR_OK,
714                           "Launched." );
715     }
717     return 0;
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 )
732     {
733         osc_server->send( lo_message_get_source( msg ), "/error",
734                           path,
735                           ERR_NO_SESSION_OPEN,
736                           "Sorry, but there's no session open for this application to join." );
737         return 0;
738     }
740     bool expected_client = false;
742     Client *c = NULL;
744     for ( std::list<Client*>::iterator i = client.begin();
745           i != client.end();
746           ++i )
747     {
748         if ( ! strcmp( (*i)->executable_path, executable_path ) &&
749              ! (*i)->active &&
750              (*i)->pending_command() == COMMAND_START )
751         {
752             // I think we've found the slot we were looking for.
753             MESSAGE( "Client was expected." );
754             c = *i;
755             break;
756         }
757     } 
758     
759     if ( ! c )
760     {
761         c = new Client();
762         c->executable_path = strdup( executable_path );
763         c->client_id = generate_client_id( c );
764     }
765     else
766         expected_client = true;
767    
768     if ( major > NSM_API_VERSION_MAJOR )
769     {
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",
773                           path,
774                           ERR_INCOMPATIBLE_API,
775                           "Server is using an incompatible API version." );
777         return 0;
778     }
780     c->pid = pid;
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 );
784     c->active = true;
786     MESSAGE( "Process has pid: %i", pid );
787     
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",
794                       path,
795                       expected_client ? 
796                       "Howdy, what took you so long?" :
797                       "Well hello, stranger. Welcome to the party.",
798                       APP_TITLE,
799                       ":server-control:broadcast:" );
801     if ( gui_is_active )
802     {
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 );
808     }
810     {
811         char *full_client_id;
812         asprintf( &full_client_id, "%s.%s", c->name, c->client_id );
813         
814         char *client_project_path = get_client_project_path( session_path, c );
815         
816         osc_server->send( lo_message_get_source( msg ), "/nsm/client/open", client_project_path, session_name, full_client_id );
817         
818         c->pending_command( COMMAND_OPEN );
820         free( full_client_id );
821         free( client_project_path );
822     }
824     return 0;
827 void
828 save_session_file ( )
830     char *session_file = NULL;
831     asprintf( &session_file, "%s/session.nsm", session_path );
832   
833     FILE *fp = fopen( session_file, "w+" );
835     /* FIXME: handle errors. */
837     for ( std::list<Client*>::iterator i = client.begin();
838           i != client.end();
839           ++i )
840     {
841         fprintf( fp, "%s:%s:%s\n", (*i)->name, (*i)->executable_path, (*i)->client_id );
842     } 
844     fclose( fp );
847 Client *
848 client_by_name ( const char *name,
849                  std::list<Client*> *cl )
851     for ( std::list<Client*>::iterator i = cl->begin();
852           i != cl->end();
853           ++i )
854     {
855         if ( !strcmp( name, (*i)->name ) )
856             return *i;
857     }
859     return NULL;
863 bool
864 dumb_clients_are_alive ( )
866     std::list<Client*> *cl = &client;
867     
868     for ( std::list<Client*>::iterator i = cl->begin();
869           i != cl->end();
870           ++i )
871     {
872         if ( (*i)->is_dumb_client() && (*i)->pid > 0 )
873             return true;
874     }
876     return false;
879 void
880 wait_for_dumb_clients_to_die ( )
882     struct signalfd_siginfo fdsi;
883     
884     MESSAGE( "Waiting for any dumb clients to die." );
885     
886     for ( int i = 0; i < 6; i++ )
887     {
888         MESSAGE( "Loop %i", i );
890         if ( ! dumb_clients_are_alive() )
891             break;
892         
893         ssize_t s = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo));
894         
895         if (s == sizeof(struct signalfd_siginfo))
896         {
897             if (fdsi.ssi_signo == SIGCHLD)
898                 handle_sigchld();
899         }
900         
901         usleep( 50000 );
902     }
904     /* FIXME: give up on remaining clients and purge them */
908 bool
909 killed_clients_are_alive ( )
911     std::list<Client*> *cl = &client;
912     
913     for ( std::list<Client*>::iterator i = cl->begin();
914           i != cl->end();
915           ++i )
916     {
917         if ( ( (*i)->pending_command() == COMMAND_QUIT ||
918                (*i)->pending_command() == COMMAND_KILL ) &&
919              (*i)->pid > 0 )
920             return true;
921     }
923     return false;
926 void
927 wait_for_killed_clients_to_die ( )
929     struct signalfd_siginfo fdsi;
930     
931     MESSAGE( "Waiting for killed clients to die." );
932     
933     for ( int i = 0; i < 24; i++ )
934     {
935         MESSAGE( "Loop %i", i );
937         if ( ! killed_clients_are_alive() )
938             break;
939         
940         ssize_t s = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo));
941         
942         if (s == sizeof(struct signalfd_siginfo))
943         {
944             if (fdsi.ssi_signo == SIGCHLD)
945                 handle_sigchld();
946         }
947         
948         purge_dead_clients();
950         usleep( 200 * 1000 );
951     }
953     if ( killed_clients_are_alive() )
954     {
955         WARNING( "Killed clients are still alive" );
956     /* FIXME: give up on remaining clients and purge them */
957     }
958     else
959         MESSAGE( "All clients have died." );
964 void
965 command_all_clients_to_save ( )
967     if ( session_path )
968     {
969         MESSAGE( "Commanding attached clients to save." );
970         
971         for ( std::list<Client*>::iterator i = client.begin();
972               i != client.end();
973               ++i )
974         {
975             command_client_to_save( *i );
976         }
977     
978         wait_for_replies();
979     
980         save_session_file();
981     }
985 void
986 command_client_to_quit ( Client *c )
988     MESSAGE( "Commanding %s to quit", c->name );
989     
990     if ( c->active )
991     {
992         c->pending_command( COMMAND_QUIT );
994         kill( c->pid, SIGTERM );
996         if ( gui_is_active )
997             osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "quit" );
998     }
999     else if ( c->is_dumb_client() )
1000     {
1001         if ( c->pid > 0 )
1002         {
1003             if ( gui_is_active )
1004                 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "kill" );
1005             
1006             /* should be kill? */
1007             c->pending_command( COMMAND_KILL );
1008     
1009             // this is a dumb client... try and kill it
1010             kill( c->pid, SIGTERM );
1011         }
1012         else
1013         {
1014             if ( gui_is_active )
1015                 osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "removed" );
1016         }
1017     }
1020 void
1021 close_session ( )
1023     if ( ! session_path )
1024         return;
1026     for ( std::list<Client*>::iterator i = client.begin();
1027           i != client.end();
1028           ++i )
1029     {
1030         command_client_to_quit( *i );
1031     }
1033     wait_for_killed_clients_to_die();
1035     purge_inactive_clients();
1036     
1037     clear_clients();
1039     if ( session_path )
1040     {
1041         char *session_lock;
1042         asprintf( &session_lock, "%s/.lock", session_path );
1044         release_lock( &session_lock_fd, session_lock );
1046         free(session_lock);
1048         free(session_path);
1049         session_path = NULL;
1050         free(session_name);
1051         session_name = NULL;
1052     }
1054     if ( gui_is_active )
1055     {
1056         osc_server->send( gui_addr, "/nsm/gui/session/name", "" );
1057     }
1060 void
1061 tell_client_session_is_loaded( Client *c )
1063     if ( c->active )
1064 //!c->is_dumb_client() )
1065     {
1066         MESSAGE( "Telling client %s that session is loaded.", c->name );
1067         osc_server->send( c->addr, "/nsm/client/session_is_loaded" );
1068     }
1071 void
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();
1077           i != client.end();
1078           ++i )
1079     {
1080         tell_client_session_is_loaded( *i );
1081     }
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 );
1091     
1092     if ( ! acquire_lock( &session_lock_fd, session_lock ) )
1093     {
1094         free( session_file );
1095         free( session_lock );
1097         WARNING( "Session is locked by another process" );
1098         return ERR_SESSION_LOCKED;
1099     }
1102     FILE *fp;
1104     if ( ! ( fp = fopen( session_file, "r" ) ) )
1105     {
1106         free( session_file );
1107         return ERR_CREATE_FAILED;
1108     }
1110     session_path = strdup( path );
1112     set_name( path );
1114     std::list<Client*> new_clients;
1116     {
1117         char * client_name = NULL;
1118         char * client_executable = NULL;
1119         char * client_id = NULL;
1120         
1121         // load the client list
1122         while ( fscanf( fp, "%a[^:]:%a[^:]:%a[^:\n]\n", &client_name, &client_executable, &client_id ) > 0 )
1123         {
1124             Client *c = new Client();
1125             
1126             c->name = client_name;
1127             c->executable_path = client_executable;
1128             c->client_id =  client_id;
1129             
1130             new_clients.push_back( c );
1131         }
1132     }
1134     MESSAGE( "Commanding unneeded and dumb clients to quit" );
1136     for ( std::list<Client*>::iterator i = client.begin();
1137           i != client.end();
1138           ++i )
1139     {
1140         if ( ! (*i)->is_capable_of( ":switch:" )
1141              ||
1142              ! client_by_name( (*i)->name, &new_clients ) )
1143         {
1144             command_client_to_quit( *i );
1145         }
1146     }
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();
1157           i != client.end();
1158           ++i )
1159     {
1160         (*i)->pre_existing = true;
1161     }    
1163     MESSAGE( "Commanding smart clients to switch" );
1165     for ( std::list<Client*>::iterator i = new_clients.begin();
1166           i != new_clients.end();
1167           ++i )
1168     {
1169         Client *c = NULL;
1170             
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 );
1175         
1176         if ( ! c )
1177             c = client_by_name( (*i)->name, &client );
1178         
1179         if ( c && c->pre_existing && !c->reply_pending() )
1180         {
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 );
1183         }
1184         else
1185         {
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 );
1193         }
1194     }
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
1201      * yet. */
1203 //    osc_server->wait( 3000 );
1205     wait_for_replies();
1207     tell_all_clients_session_is_loaded();
1209     MESSAGE( "Loaded." );
1211     new_clients.clear();
1213     if ( gui_is_active )
1214     {
1215         osc_server->send( gui_addr, "/nsm/gui/session/name", session_name );
1216     }
1217     
1218     return ERR_OK;
1221 OSC_HANDLER( save )
1223     if ( ! session_path )
1224     {
1225         osc_server->send( lo_message_get_source( msg ), "/error", path, 
1226                           ERR_NO_SESSION_OPEN,
1227                           "No session to save.");
1228                           
1229         return 0;
1230     }
1231     
1232     command_all_clients_to_save();
1234     MESSAGE( "Done." );
1236     osc_server->send( lo_message_get_source( msg ), "/reply", path, "Saved." );
1238     return 0;
1241 OSC_HANDLER( duplicate )
1243     if ( ! session_path )
1244     {
1245         osc_server->send( lo_message_get_source( msg ), "/error", path, 
1246                           ERR_NO_SESSION_OPEN,
1247                           "No session to duplicate.");
1248                           
1249         return 0;
1250     }
1252     if ( ! path_is_valid( &argv[0]->s ) )
1253     {
1254         osc_server->send( lo_message_get_source( msg ), "/error", path,
1255                           ERR_CREATE_FAILED,
1256                           "Invalid session name." );
1258         return 0;
1259     }     
1261     command_all_clients_to_save();
1262     
1263     if ( clients_have_errors() )
1264     {
1265         osc_server->send( lo_message_get_source( msg ), "/error", path,
1266                           ERR_GENERAL_ERROR,
1267                           "Some clients could not save" );
1269         return 0;
1270     }
1272 //    save_session_file();
1274     char *spath;
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 */
1281     char *cmd;
1282     asprintf( &cmd, "cp -R \"%s\" \"%s\"", session_path, spath);
1284     system( cmd );
1286     free( cmd );
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 ) )
1293     {
1294         MESSAGE( "Loaded" );
1295         osc_server->send( lo_message_get_source( msg ), "/reply", path,
1296                           "Loaded." );
1297     }
1298     else
1299     {
1300         MESSAGE( "Failed" );
1301         osc_server->send( lo_message_get_source( msg ), "/error", path,
1302                           ERR_NO_SUCH_FILE,
1303                           "No such file." );
1304     }
1306     free( spath );
1308     MESSAGE( "Done" );
1311     osc_server->send( lo_message_get_source( msg ), "/reply", path, "Duplicated." );
1313     return 0;
1316 OSC_HANDLER( new )
1318     if ( ! path_is_valid( &argv[0]->s ) )
1319     {
1320         osc_server->send( lo_message_get_source( msg ), "/error", path,
1321                           ERR_CREATE_FAILED,
1322                           "Invalid session name." );
1324         return 0;
1325     }     
1327     if ( session_path )
1328     {
1329         command_all_clients_to_save();
1330         
1331         close_session();
1332     }
1334     MESSAGE( "Creating new session" );
1337     char *spath;
1338     asprintf( &spath, "%s/%s", session_root, &argv[0]->s );
1339    
1340     if ( mkpath( spath, true ) )
1341     {
1342         osc_server->send( lo_message_get_source( msg ), "/error", path,
1343                           ERR_CREATE_FAILED,
1344                           "Could not create the session directory" );
1346         free(spath);
1348         return 0;
1349     }
1351     session_path = strdup( spath );
1352     
1353     set_name( session_path );
1355     osc_server->send( lo_message_get_source( msg ), "/reply", path, "Created." );
1357     if ( gui_is_active )
1358     {
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  );
1361     }
1363     save_session_file();
1365     free( spath );
1367     osc_server->send( lo_message_get_source( msg ), "/reply", path,
1368                               "Session created" );
1369         
1370     return 0;
1373 static lo_address list_response_address;
1376 list_file ( const char *fpath, const struct stat *sb, int tflag )
1378     char *s;
1380     if ( tflag == FTW_F )
1381     {
1382         s = strdup( fpath );
1383         if ( ! strcmp( "session.nsm", basename( s ) ) )
1384         {
1385             free( s );
1386             s = strdup( fpath );
1388             s = dirname( s );
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  );
1394             free( s );
1395         }
1396         else
1397             free( s );
1398     }
1400     return 0;
1403 OSC_HANDLER( list )
1405     MESSAGE( "Listing sessions" );
1407     list_response_address = lo_message_get_source( msg );
1408     
1409     ftw( session_root, list_file, 20 );
1411     osc_server->send( lo_message_get_source( msg ), path,
1412                       ERR_OK,
1413                       "Done." );
1415     return 0;
1418 OSC_HANDLER( open )
1420     DMESSAGE( "Got open" );
1422     if ( session_path )
1423     {
1424         command_all_clients_to_save();
1425         
1426         if ( clients_have_errors() )
1427         {
1428             osc_server->send( lo_message_get_source( msg ), "/error", path,
1429                               ERR_GENERAL_ERROR,
1430                               "Some clients could not save" );
1431             return 0;
1432         }
1433         
1434 //        save_session_file();
1435     }
1437     char *spath;
1438     asprintf( &spath, "%s/%s", session_root, &argv[0]->s );
1440     MESSAGE( "Attempting to open %s", spath );
1442     int err = load_session_file( spath );
1443         
1444     if ( ! err )
1445     {
1446         MESSAGE( "Loaded" );
1447         osc_server->send( lo_message_get_source( msg ), "/reply", path,
1448                           "Loaded." );
1449     }
1450     else
1451     {
1452         MESSAGE( "Failed" );
1453         const char *m = NULL;
1454         switch ( err )
1455         {
1456             case ERR_CREATE_FAILED:
1457                 m = "Could not create session file!";
1458                 break;
1459             case ERR_SESSION_LOCKED:
1460                 m = "Session is locked by another process!";
1461                 break;
1462             case ERR_NO_SUCH_FILE:
1463                 m = "The named session does not exist.";
1464                 break;
1465             default:
1466                 m = "Unknown error";
1467         }
1470         osc_server->send( lo_message_get_source( msg ), "/error", path,
1471                           err,
1472                           m );
1473     }
1475     free( spath );
1477     MESSAGE( "Done" );
1479     return 0;
1483 OSC_HANDLER( quit )
1485     close_session();
1487     exit(0);
1489     return 0;
1492 OSC_HANDLER( abort )
1494     if ( ! session_path )
1495     {
1496         osc_server->send( lo_message_get_source( msg ), "/error", path,
1497                           ERR_NO_SESSION_OPEN,
1498                           "No session to abort." );
1500         return 0;
1501     }
1503     MESSAGE( "Commanding attached clients to quit." );
1505     close_session();
1507     osc_server->send( lo_message_get_source( msg ), "/reply", path,
1508                       "Aborted." );
1509                       
1510     MESSAGE( "Done" );
1512     return 0;
1515 OSC_HANDLER( close )
1517     if ( ! session_path )
1518     {
1519         osc_server->send( lo_message_get_source( msg ), "/error", path,
1520                           ERR_NO_SESSION_OPEN,
1521                           "No session to close." );
1523         return 0;
1524     }
1526     command_all_clients_to_save();
1528     MESSAGE( "Commanding attached clients to quit." );
1530     close_session();
1532     osc_server->send( lo_message_get_source( msg ), "/reply", path,
1533                       "Closed." );
1534                       
1535     MESSAGE( "Done" );
1537     return 0;
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/" ) ) )
1547         return 0;
1549     std::list<OSC::OSC_Value> new_args;
1551     for ( int i = 1; i < argc; ++i )
1552     {
1553         switch ( types[i] )
1554         {
1555             case 's':
1556                 new_args.push_back( OSC::OSC_String( &argv[i]->s ) );
1557                 break;
1558             case 'i':
1559                 new_args.push_back( OSC::OSC_Int( argv[i]->i ) );
1560                 break;
1561             case 'f':
1562                 new_args.push_back( OSC::OSC_Float( argv[i]->f ) );
1563                 break;
1564         }
1565     }
1567     char *sender_url = lo_address_get_url( lo_message_get_source( msg ) );
1569     for ( std::list<Client*>::iterator i = client.begin();
1570           i != client.end();
1571           ++i )
1572     {
1573         if ( ! (*i)->addr )
1574             continue;
1576         char *url = lo_address_get_url( (*i)->addr );
1578         if ( strcmp( sender_url, url ) )
1579         {
1580             osc_server->send( (*i)->addr, to_path, new_args );
1581         }
1583         free( url );
1584     }
1586     /* also relay to attached GUI so that the broadcast can be
1587      * propagated to another NSMD instance */
1588     if ( gui_is_active )
1589     {
1590         
1591         char *u1 = lo_address_get_url( gui_addr );
1592      
1593         if ( strcmp( u1, sender_url ) )
1594         {
1595             new_args.push_front( OSC::OSC_String( to_path ) );
1596             
1597             osc_server->send( gui_addr, path, new_args );
1598         }
1599         
1600         free(u1);
1601     }
1602     
1603     free( sender_url );
1605     return 0;
1610 /*********************************/
1611 /* Client Informational Messages */
1612 /*********************************/
1614 OSC_HANDLER( progress )
1616     Client *c = get_client_by_address( lo_message_get_source( msg ) );
1618     if ( c )
1619     {
1620         c->progress = argv[0]->f;
1622         MESSAGE( "%s progress: %i%%", c->name, (int)(c->progress * 100.0f) );
1624         if ( gui_is_active )
1625         {
1626             osc_server->send( gui_addr, "/nsm/gui/client/progress", c->client_id, (float)c->progress );
1627         }
1628     }
1630     return 0;
1633 OSC_HANDLER( is_dirty )
1635     MESSAGE( "Client sends dirty" );
1637     Client *c = get_client_by_address( lo_message_get_source( msg ) );
1638     
1639     if ( ! c )
1640         return 0;
1642     c->dirty = 1;
1644     if ( gui_is_active )
1645         osc_server->send( gui_addr, "/nsm/gui/client/dirty", c->client_id, c->dirty );
1647     return 0;
1650 OSC_HANDLER( is_clean )
1652     MESSAGE( "Client sends clean" );
1654     Client *c = get_client_by_address( lo_message_get_source( msg ) );
1656     if ( ! c )
1657         return 0;
1659     c->dirty = 0;
1661     if ( gui_is_active )
1662         osc_server->send( gui_addr, "/nsm/gui/client/dirty", c->client_id, c->dirty );
1664     return 0;
1667 OSC_HANDLER( gui_is_hidden )
1669     MESSAGE( "Client sends gui hidden" );
1671     Client *c = get_client_by_address( lo_message_get_source( msg ) );
1673     if ( ! c )
1674         return 0;
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() );
1681     return 0;
1684 OSC_HANDLER( gui_is_shown )
1686     MESSAGE( "Client sends gui shown" );
1688     Client *c = get_client_by_address( lo_message_get_source( msg ) );
1690     if ( ! c )
1691         return 0;
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() );
1698     return 0;
1701 OSC_HANDLER( message )
1703     Client *c = get_client_by_address( lo_message_get_source( msg ) );
1705     if ( ! c )
1706         return 0;
1708     if ( gui_is_active )
1709         osc_server->send( gui_addr, "/nsm/gui/client/message", c->client_id, argv[0]->i, &argv[1]->s );
1711     return 0;
1714 OSC_HANDLER( label )
1716     Client *c = get_client_by_address( lo_message_get_source( msg ) );
1718     if ( ! c )
1719         return 0;
1721     if ( strcmp( types, "s" ) )
1722         return -1;
1723     
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 );
1729     return 0;
1732 /**********************/
1733 /* Response Handlers  */
1734 /**********************/
1737 OSC_HANDLER( error )
1739     Client *c = get_client_by_address( lo_message_get_source( msg ) );
1741     if ( ! c )
1742     {
1743         WARNING( "Error from unknown client" );
1744         return 0;
1745     }
1747 //    const char *rpath = &argv[0]->s;
1749     int err_code = argv[1]->i;
1750     
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 );
1757     
1758     if ( gui_is_active )
1759         osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "error" );
1761     return 0;
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;
1771     
1772     if ( c )
1773     {
1774         c->set_reply( ERR_OK, message );
1776         MESSAGE( "Client \"%s\" replied with: %s in %fms", c->name, message, c->milliseconds_since_last_command() ); 
1777         
1778         c->pending_command( COMMAND_NONE );
1779         
1780         if ( gui_is_active )
1781             osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "ready" );
1782     }
1783     else
1784         MESSAGE( "Reply from unknown client" );
1786     return 0;
1791 /******************/
1792 /* GUI operations */
1793 /******************/
1796 OSC_HANDLER( stop )
1798     Client *c = get_client_by_id( &client, &argv[0]->s );
1800     if ( c )
1801     {
1802         if ( c->pid != 0 )
1803         {
1804             kill( c->pid, SIGTERM );
1806             if ( gui_is_active )
1807                 osc_server->send( gui_addr, "/reply", "Client stopped." );
1808         }
1809     }
1810     else
1811     {
1812         if ( gui_is_active )
1813             osc_server->send( gui_addr, "/error", -10, "No such client." );
1814     }
1816    
1817     return 0;
1820 OSC_HANDLER( remove )
1822     Client *c = get_client_by_id( &client, &argv[0]->s );
1824     if ( c )
1825     {
1826         if ( c->pid == 0 &&
1827              ! c->active )
1828         {
1829             osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "removed" );
1830             
1831             client.remove( c );
1832             
1833             delete c;
1835             if ( gui_is_active )
1836                 osc_server->send( gui_addr, "/reply", "Client removed." );
1837         }
1838     }
1839     else
1840     {
1841         if ( gui_is_active )
1842             osc_server->send( gui_addr, "/error", -10, "No such client." );
1843     }
1845    
1846     return 0;
1849 OSC_HANDLER( resume )
1851     Client *c = get_client_by_id( &client, &argv[0]->s );
1852     
1853     /* FIXME: return error if no such client? */
1854     if ( c )
1855     {
1856         if ( c->pid == 0 &&
1857              ! c->active )
1858         {
1859             if ( ! launch( c->executable_path, c->client_id ) )
1860             {
1861                 
1862             }
1863         }
1864     }
1865     
1866     return 0;
1870 OSC_HANDLER( client_save )
1872     Client *c = get_client_by_id( &client, &argv[0]->s );
1874     /* FIXME: return error if no such client? */
1875     if ( c )
1876     {
1877         if ( c->active )
1878         {
1879             command_client_to_save( c );
1880         }
1881     }
1883     return 0;
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? */
1891     if ( c )
1892     {
1893         if ( c->active )
1894         {
1895             osc_server->send( c->addr, "/nsm/client/show_optional_gui" );
1896         }
1897     }
1899     return 0;
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? */
1907     if ( c )
1908     {
1909         if ( c->active )
1910         {
1911             osc_server->send( c->addr, "/nsm/client/hide_optional_gui" );
1912         }
1913     }
1915     return 0;
1918 void
1919 announce_gui( const char *url, bool is_reply )
1921     gui_addr = lo_address_new_from_url( url );
1922     gui_is_active = true;
1924     if ( is_reply )
1925         osc_server->send( gui_addr, "/nsm/gui/gui_announce", "hi" );
1926     else
1927         osc_server->send( gui_addr, "/nsm/gui/server_announce", "hi" );
1929     for ( std::list<Client*>::iterator i = client.begin();
1930           i != client.end();
1931           ++i )
1932     {
1933         Client *c = *i;
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 );
1938     }
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 );
1950     return 0;
1953 OSC_HANDLER( ping )
1955     osc_server->send( lo_message_get_source( msg ), "/reply", path );
1957     return 0;
1963 int main(int argc, char *argv[])
1965     sigset_t mask;
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 */
1975     {
1976         time_t seconds;
1977         time(&seconds);
1978         
1979         srand( (unsigned int) seconds );
1980     }
1982 //    char *osc_port = "6666";
1983     char *osc_port = NULL;
1984     const char *gui_url = NULL;
1986     static struct option long_options[] = 
1987     {
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' },
1993         { 0, 0, 0, 0 }
1994     };
1996     int option_index = 0;
1997     int c = 0;
1999     bool detach = false;
2001     while ( ( c = getopt_long_only( argc, argv, "", long_options, &option_index  ) ) != -1 )
2002     {
2003         switch ( c )
2004         {
2005             case 'd':
2006                 detach = true;
2007                 break;
2008             case 's':
2009                 session_root = optarg;
2010                 break;
2011             case 'p':
2012                 DMESSAGE( "Using OSC port %s", optarg );
2013                 osc_port = optarg;
2014                 break;
2015             case 'g':                
2016                 DMESSAGE( "Going to connect to GUI at: %s", optarg );
2017                 gui_url = optarg;
2018                 break;
2019             case 'h':
2020                 printf( "Usage: %s [--osc-port portnum] [--session-root path]\n\n", argv[0] );
2021                 exit(0);
2022                 break;
2023         }
2024     }
2026     if ( !session_root )
2027         asprintf( &session_root, "%s/%s", getenv( "HOME" ), "NSM Sessions" );
2028   
2029     struct stat st;
2031     if ( stat( session_root, &st ) )
2032     {
2033         if ( mkdir( session_root, 0771 ) )
2034         {
2035             FATAL( "Failed to create session directory: %s", strerror( errno ) );
2036         }
2037     }
2039     MESSAGE( "Session root is: %s", session_root );
2041     osc_server = new OSC::Endpoint();
2043     if ( osc_server->init( LO_UDP, osc_port ) )
2044     {
2045         FATAL( "Failed to create OSC server." );
2046     }
2048     printf( "NSM_URL=%s\n", osc_server->url() );
2050     if ( gui_url )
2051     {
2052         announce_gui( gui_url, false );
2053     }
2055     /*  */
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" );
2061   
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" );
2069     
2070     /*  */
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, "" );
2093     if ( detach )
2094     {
2095         MESSAGE( "Detaching from console" );
2096         if ( fork() )
2097         {
2098             exit( 0 );            
2099         }
2100         else
2101         {
2102             fclose( stdin );
2103             fclose( stdout );
2104             fclose( stderr );
2105         }
2106     }
2108     struct signalfd_siginfo fdsi;
2111     /* listen for sigchld signals and process OSC messages forever */
2112     for ( ;; )
2113     {
2114         ssize_t s = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo));
2115         
2116         if (s == sizeof(struct signalfd_siginfo))
2117         {
2118             if (fdsi.ssi_signo == SIGCHLD)
2119                 handle_sigchld();
2120         }
2121         
2122         osc_server->wait( 1000 );
2124         purge_dead_clients();
2125     }
2126     
2127 //    osc_server->run();
2129     return 0;