Update NTK.
[nondaw.git] / session-manager / src / nsm-proxy.C
blob65cd45be6e1530e213fa25abbad499f3a376a018
2 /*******************************************************************************/
3 /* Copyright (C) 2012 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 #pragma GCC diagnostic ignored "-Wunused-parameter"
22 #define _MODULE_ "nsm-proxy"
23 #define APP_NAME "NSM Proxy"
24 #define APP_TITLE "NSM Proxy"
26 #include "debug.h"
28 #include <lo/lo.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <signal.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/signalfd.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
41 static lo_server losrv;
42 static lo_address nsm_addr;
43 static lo_address gui_addr;
44 static int nsm_is_active;
45 static char *project_file;
46 static int die_now = 0;
47 static int signal_fd;
49 static char *nsm_client_id;
50 static char *nsm_display_name;
52 #define CONFIG_FILE_NAME "nsm-proxy.config"
54 class NSM_Proxy {
56     char *_label;
57     char *_executable;
58     char *_arguments;
59     int _save_signal;
60     int _pid;
62 public:
64     NSM_Proxy ( )
65         {
66             _label = _executable = _arguments = 0;
67             _save_signal = 0;
68             _pid = 0;
69         }
71     ~NSM_Proxy ( )
72         {
73         }
74     
75     void kill ( void )
76         {
77             if ( _pid )
78                 ::kill( _pid, SIGTERM );
79         }
81     bool start ( const char *executable, const char *arguments )
82         {
83             if ( _executable )
84                 free( _executable );
85             if ( _arguments )
86                 free( _arguments );
88             _executable = strdup( executable );
90             if ( arguments )
91                 _arguments = strdup( arguments );
92             else
93                 _arguments = NULL;
95             return start();
96         }
98     bool start ( void )
99         {
100             if ( _pid )
101                 /* already running */
102                 return true;
104             int pid;
105             if ( ! (pid = fork()) )
106             {
107                 MESSAGE( "Launching %s\n", _executable );
108                 
109 //                char *args[] = { strdup( executable ), NULL };
111                 char *cmd;
113                 if ( _arguments )
114                     asprintf( &cmd, "exec %s %s", _executable, _arguments );
115                 else
116                     asprintf( &cmd, "exec %s", _executable );
118                 char *args[] = { _executable, strdup( "-c" ), cmd, NULL };
119                 
120                 setenv( "NSM_CLIENT_ID", nsm_client_id, 1 );
121                 setenv( "NSM_SESSION_NAME", nsm_display_name, 1 );
122                 
123                 if ( -1 == execvp( "/bin/sh", args ) )
124                 {
125                     WARNING( "Error starting process: %s", strerror( errno ) );
126                     
127                     exit(-1);
128                 }
129             }
131             _pid = pid;
133             return _pid > 0;
134         }
136     void save_signal ( int s )
137         {
138             _save_signal = s;
139         }
140     
141     void label ( const char *s )
142         {
143             if ( _label )
144                 free( _label );
146             _label = strdup( s );
148             lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/nsm/client/label", "s", _label );
149         }
151     void save ( void )
152         {
153             DMESSAGE( "Sending process save signal" );
154             if ( _pid )
155                 ::kill( _pid, _save_signal );
156         }
159     bool dump ( const char *path )
160         {
161             FILE *fp = fopen( path, "w" );
163             if ( !fp )
164             {
165                 WARNING( "Error opening file for saving: %s", strerror( errno ) );
166                 return false;
167             }
169             if ( _executable && strlen(_executable) )
170                 fprintf( fp, "executable\n\t%s\n", _executable );
172             if ( _arguments && strlen(_arguments) )
173                 fprintf( fp, "arguments\n\t%s\n", _arguments );
175             fprintf( fp, "save signal\n\t%i\n", _save_signal );
176             
177             if ( _label && strlen(_label) )
178                 fprintf( fp, "label\n\t%s\n", _label );
180             fclose( fp );
182             return true;
183         }
185     bool restore ( const char *path )
186         {
187             FILE *fp = fopen( path, "r" );
189             if ( ! fp )
190             {
191                 WARNING( "Error opening file for restore: %s", strerror( errno ) );
192                 return false;
193             }
195             char *name;
196             char *value;
198             MESSAGE( "Loading file config \"%s\"", path ); 
200             while ( 2 == fscanf( fp, "%a[^\n]\n\t%a[^\n]\n", &name, &value ) )
201             {
203                 DMESSAGE( "%s=%s", name, value );
204                 
205                 if ( !strcmp( name, "executable" ) )
206                     _executable = value;
207                 else if (!strcmp( name, "arguments" ) )
208                     _arguments = value;
209                 else if ( !strcmp( name, "save signal" ) )
210                 {
211                     _save_signal = atoi( value );
212                     free( value );
213                 }
214                 else if ( !strcmp( name, "label" ) )
215                 {
216                     label( value );
217                     free( value );
218                 }
219                 else
220                 {
221                     WARNING( "Unknown option \"%s\" in config file", name );
222                 }
224                 free( name );
225             }
227             fclose( fp );
229             start();
231             return true;
232         }
234     void update ( lo_address to )
235         {
236             DMESSAGE( "Sending update" );
238             lo_send_from( to, losrv, LO_TT_IMMEDIATE, "/nsm/proxy/save_signal", "i",  _save_signal );
239             lo_send_from( to, losrv, LO_TT_IMMEDIATE, "/nsm/proxy/label", "s", _label ? _label : "" );
240             lo_send_from( to, losrv, LO_TT_IMMEDIATE, "/nsm/proxy/executable", "s", _executable ? _executable : "" );
241             lo_send_from( to, losrv, LO_TT_IMMEDIATE, "/nsm/proxy/arguments", "s", _arguments ? _arguments : "" );
242         }
245 NSM_Proxy *nsm_proxy;
247 void
248 announce ( const char *nsm_url, const char *client_name, const char *process_name )
250     printf( "Announcing to NSM\n" );
252     lo_address to = lo_address_new_from_url( nsm_url );
254     int pid = (int)getpid();
256     lo_send_from( to, losrv, LO_TT_IMMEDIATE, "/nsm/server/announce", "sssiii",
257                   client_name,
258                   ":optional-gui:",
259                   process_name,
260                   1, /* api_major_version */
261                   0, /* api_minor_version */
262                   pid );
264     lo_address_free( to );
267 bool
268 snapshot ( const char *file )
270     /* mkdir( file, 0777 ); */
271     
272     char *path;
273     asprintf( &path, "%s/%s", file, CONFIG_FILE_NAME );
275     bool r = nsm_proxy->dump( path );
277     free( path );
279     return r;
282 bool
283 open ( const char *file )
285     char *path;
286     asprintf( &path, "%s/%s", file, CONFIG_FILE_NAME );
288     bool r = nsm_proxy->restore( path );
290     free( path );
292     return r;
295 /****************/
296 /* OSC HANDLERS */
297 /****************/
299 /* NSM */
302 osc_announce_error ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
304     if ( strcmp( types, "sis" ) )
305         return -1;
307     if ( strcmp( "/nsm/server/announce", &argv[0]->s ) )
308          return -1;
310     printf( "Failed to register with NSM: %s\n", &argv[2]->s );
311     nsm_is_active = 0;
313     return 0;
318 osc_announce_reply ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
320     if ( strcmp( "/nsm/server/announce", &argv[0]->s ) )
321          return -1;
323     printf( "Successfully registered. NSM says: %s", &argv[1]->s );
324     
325     nsm_is_active = 1;
326     nsm_addr = lo_address_new_from_url( lo_address_get_url( lo_message_get_source( msg ) ) );
327     
328     return 0;
332 osc_save ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
334     bool r = snapshot( project_file );
336     nsm_proxy->save();
338     if ( r )
339         lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/reply", "ss", path, "OK" );
340     else
341         lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/error", "sis", path, -1, "Error saving project file" );
343     return 0;
346 static int gui_pid;
348 void
349 show_gui ( void )
352     int pid;
353     if ( ! (pid = fork()) )
354     {
355         char executable[] = "nsm-proxy-gui";
357         MESSAGE( "Launching %s\n", executable );
358         
359         char *url = lo_server_get_url( losrv );
361         char *args[] = { executable, strdup( "--connect-to" ), url, NULL };
362         
363         if ( -1 == execvp( executable, args ) )
364         {
365             WARNING( "Error starting process: %s", strerror( errno ) );
366             
367             exit(-1);
368         }
369     }
370     
371     gui_pid = pid;
373     lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/nsm/client/gui_is_shown", "" );
377 osc_show_gui ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
379     show_gui();
381     /* FIXME: detect errors */
383     lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/reply", "ss", path, "OK" );
385     return 0;
388 void
389 hide_gui ( void )
391     if ( gui_pid )
392     {
393         kill( gui_pid, SIGTERM );
394     }
398 osc_hide_gui ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
400     hide_gui();
402     lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/nsm/client/gui_is_hidden", "" );
404     /* FIXME: detect errors */
406     lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/reply", "ss", path, "OK" );
408     return 0;
412 osc_open ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
414     const char *new_path = &argv[0]->s;
415     const char *display_name = &argv[1]->s;
416     const char *client_id = &argv[2]->s;
418     if ( nsm_client_id )
419         free(nsm_client_id);
421     nsm_client_id = strdup( client_id );
423     if ( nsm_display_name )
424         free( nsm_display_name );
426     nsm_display_name = strdup( display_name );
428     char *new_filename;
430     mkdir( new_path, 0777 );
432     chdir( new_path );
433     
434     asprintf( &new_filename, "%s/%s", new_path, CONFIG_FILE_NAME );
436     struct stat st;
438     if ( 0 == stat( new_filename, &st ) )
439     {
440         if ( open( new_path ) )
441         {
442         }
443         else
444         {
445             lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/error", "sis", path, -1, "Could not open file" );
446             return 0;
447         }
449         lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/nsm/client/gui_is_hidden", "" );
450     }
451     else
452     {
453         show_gui();
454     }
456     if ( project_file )
457         free( project_file );
458     
459     project_file = strdup( new_path );
461 // new_filename;
463     lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/reply", "ss", path, "OK" );
465     if ( gui_addr )
466         nsm_proxy->update( gui_addr );
468     return 0;
473 /* GUI */
476 osc_label ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
478     nsm_proxy->label( &argv[0]->s );
480     return 0;
484 osc_save_signal ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
486     nsm_proxy->save_signal( argv[0]->i );
487     
488     return 0;
492 osc_start ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
494     snapshot( project_file );
496     if ( nsm_proxy->start( &argv[0]->s, &argv[1]->s ) )
497     {
498         hide_gui();
499     }
500     
501     return 0;
505 osc_kill ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
507     nsm_proxy->kill();
508     
509     return 0;
513 osc_update ( const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data )
515     lo_address to = lo_address_new_from_url( lo_address_get_url( lo_message_get_source( msg ) ));
517     nsm_proxy->update( to );
519     gui_addr = to;
521     return 0;
526 void
527 signal_handler ( int x )
529     die_now = 1;
532 void
533 set_traps ( void )
535         signal( SIGHUP, signal_handler );
536         signal( SIGINT, signal_handler );
537 //      signal( SIGQUIT, signal_handler );
538 //      signal( SIGSEGV, signal_handler );
539 //      signal( SIGPIPE, signal_handler );
540         signal( SIGTERM, signal_handler );
544 void
545 init_osc ( const char *osc_port )
547     losrv = lo_server_new( osc_port, NULL );
548 //error_handler );
550     char *url = lo_server_get_url(losrv);
551     printf("OSC: %s\n",url);
552     free(url);
554     /* NSM */
555     lo_server_add_method( losrv, "/nsm/client/save", "", osc_save, NULL );
556     lo_server_add_method( losrv, "/nsm/client/open", "sss", osc_open, NULL );
557     lo_server_add_method( losrv, "/nsm/client/show_optional_gui", "", osc_show_gui, NULL );
558     lo_server_add_method( losrv, "/nsm/client/hide_optional_gui", "", osc_hide_gui, NULL );
559     lo_server_add_method( losrv, "/error", "sis", osc_announce_error, NULL );
560     lo_server_add_method( losrv, "/reply", "ssss", osc_announce_reply, NULL );
562     /* GUI */
563     lo_server_add_method( losrv, "/nsm/proxy/label", "s", osc_label, NULL );
564     lo_server_add_method( losrv, "/nsm/proxy/save_signal", "i", osc_save_signal, NULL );
565     lo_server_add_method( losrv, "/nsm/proxy/kill", "", osc_kill, NULL );
566     lo_server_add_method( losrv, "/nsm/proxy/start", "ss", osc_start, NULL );
567     lo_server_add_method( losrv, "/nsm/proxy/update", "", osc_update, NULL );
571 void
572 die ( void )
574     if ( gui_pid )
575     {
576         DMESSAGE( "Killing GUI" );
578         kill( gui_pid, SIGTERM );
579     }
581     nsm_proxy->kill();
583     exit(0);
587 void handle_sigchld ( )
589     for ( ;; )
590     {
591         int status;
592         pid_t pid = waitpid(-1, &status, WNOHANG);
594         if (pid <= 0) 
595             break;
596         
597         if ( pid == gui_pid )
598         {
599             lo_send_from( nsm_addr, losrv, LO_TT_IMMEDIATE, "/nsm/client/gui_is_hidden", "" );
601             gui_pid = 0;
603             /* don't care... */
604             continue;
605         }
607         /* otherwise, it was our proxied process that died, so we should die too */
608         printf( "proxied process died... nsm-proxy dying too\n" );
610         die_now = 1;
611     }
615 main ( int argc, char **argv )
617     set_traps();
619     sigset_t mask;
621     sigemptyset( &mask );
622     sigaddset( &mask, SIGCHLD );
624     sigprocmask(SIG_BLOCK, &mask, NULL );
626     signal_fd = signalfd( -1, &mask, SFD_NONBLOCK );
628     nsm_proxy = new NSM_Proxy();
630     init_osc( NULL );
632     const char *nsm_url = getenv( "NSM_URL" );
634     if ( nsm_url )
635     {
636         announce( nsm_url, APP_TITLE, argv[0] );
637     }
638     else
639     {
640         fprintf( stderr, "Could not register as NSM client.\n" );
641         exit(1);
642     }
645     struct signalfd_siginfo fdsi;
647     /* listen for sigchld signals and process OSC messages forever */
648     for ( ;; )
649     {
650         ssize_t s = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo));
651         
652         if (s == sizeof(struct signalfd_siginfo))
653         {
654             if (fdsi.ssi_signo == SIGCHLD)
655                 handle_sigchld();
656         }
657         
658         lo_server_recv_noblock( losrv, 500 );
660         if ( die_now )
661             die();
662     }