Assists music production by grouping standalone programs into sessions. Community version of "Non Session Manager".
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2233 lines
52KB

  1. /*******************************************************************************/
  2. /* Copyright (C) 2010 Jonathan Moore Liles */
  3. /* */
  4. /* This program is free software; you can redistribute it and/or modify it */
  5. /* under the terms of the GNU General Public License as published by the */
  6. /* Free Software Foundation; either version 2 of the License, or (at your */
  7. /* option) any later version. */
  8. /* */
  9. /* This program is distributed in the hope that it will be useful, but WITHOUT */
  10. /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
  11. /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */
  12. /* more details. */
  13. /* */
  14. /* You should have received a copy of the GNU General Public License along */
  15. /* with This program; see the file COPYING. If not,write to the Free Software */
  16. /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  17. /*******************************************************************************/
  18. #define __MODULE__ "nsmd"
  19. #include "debug.h"
  20. #ifndef _GNU_SOURCE
  21. #define _GNU_SOURCE
  22. #endif
  23. #include <errno.h>
  24. #include <string.h>
  25. #include <list>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <unistd.h>
  29. #include <sys/types.h>
  30. #include <signal.h>
  31. #include <sys/signalfd.h>
  32. #include <sys/stat.h>
  33. #include <sys/wait.h>
  34. #include <unistd.h>
  35. #include <time.h>
  36. #include <libgen.h>
  37. #include <dirent.h>
  38. #include <ftw.h>
  39. #include <list>
  40. #include <getopt.h>
  41. #include <sys/time.h>
  42. #include <OSC/Endpoint.H>
  43. /* for locking */
  44. #include "file.h"
  45. #pragma GCC diagnostic ignored "-Wunused-parameter"
  46. static OSC::Endpoint *osc_server;
  47. static lo_address gui_addr;
  48. static bool gui_is_active = false;
  49. static int signal_fd;
  50. static int session_lock_fd = 0;
  51. static char *session_root;
  52. #define NSM_API_VERSION_MAJOR 1
  53. #define NSM_API_VERSION_MINOR 0
  54. #define ERR_OK 0
  55. #define ERR_GENERAL_ERROR -1
  56. #define ERR_INCOMPATIBLE_API -2
  57. #define ERR_BLACKLISTED -3
  58. #define ERR_LAUNCH_FAILED -4
  59. #define ERR_NO_SUCH_FILE -5
  60. #define ERR_NO_SESSION_OPEN -6
  61. #define ERR_UNSAVED_CHANGES -7
  62. #define ERR_NOT_NOW -8
  63. #define ERR_BAD_PROJECT -9
  64. #define ERR_CREATE_FAILED -10
  65. #define ERR_SESSION_LOCKED -11
  66. #define ERR_OPERATION_PENDING -12
  67. #define APP_TITLE "Non Session Manager"
  68. enum {
  69. COMMAND_NONE = 0,
  70. COMMAND_QUIT,
  71. COMMAND_KILL,
  72. COMMAND_SAVE,
  73. COMMAND_OPEN,
  74. COMMAND_START,
  75. COMMAND_CLOSE,
  76. COMMAND_DUPLICATE,
  77. COMMAND_NEW
  78. };
  79. static int pending_operation = COMMAND_NONE;
  80. struct Client
  81. {
  82. private:
  83. int _reply_errcode;
  84. char *_reply_message;
  85. int _pending_command; /* */
  86. struct timeval _command_sent_time;
  87. bool _gui_visible;
  88. char *_label;
  89. public:
  90. lo_address addr; /* */
  91. char *name; /* client application name */
  92. char *executable_path; /* path to client executable */
  93. int pid; /* PID of client process */
  94. float progress; /* */
  95. bool active; /* client has registered via announce */
  96. // bool stopped; /* the client quit, but not because we told it to--user still has to decide to remove it from the session */
  97. char *client_id; /* short part of client ID */
  98. char *capabilities; /* client capabilities... will be null for dumb clients */
  99. bool dirty; /* flag for client self-reported dirtiness */
  100. bool pre_existing;
  101. const char *status;
  102. const char *label ( void ) const { return _label; }
  103. void label ( const char *l )
  104. {
  105. if ( _label )
  106. free( _label );
  107. if ( l )
  108. _label = strdup( l );
  109. else
  110. _label = NULL;
  111. }
  112. bool gui_visible ( void ) const
  113. {
  114. return _gui_visible;
  115. }
  116. void gui_visible ( bool b )
  117. {
  118. _gui_visible = b;
  119. }
  120. bool
  121. has_error ( void ) const
  122. {
  123. return _reply_errcode != 0;
  124. }
  125. int
  126. error_code ( void ) const
  127. {
  128. return _reply_errcode;
  129. }
  130. const char * message ( void )
  131. {
  132. return _reply_message;
  133. }
  134. void
  135. set_reply ( int errcode, const char *message )
  136. {
  137. if ( _reply_message )
  138. free( _reply_message );
  139. _reply_message = strdup( message );
  140. _reply_errcode = errcode;
  141. }
  142. bool reply_pending ( void )
  143. {
  144. return _pending_command != COMMAND_NONE;
  145. }
  146. bool is_dumb_client ( void )
  147. {
  148. return capabilities == NULL;
  149. }
  150. void pending_command ( int command )
  151. {
  152. gettimeofday( &_command_sent_time, NULL );
  153. _pending_command = command;
  154. }
  155. double milliseconds_since_last_command ( void ) const
  156. {
  157. struct timeval now;
  158. gettimeofday( &now, NULL );
  159. double elapsedms = ( now.tv_sec - _command_sent_time.tv_sec ) * 1000.0;
  160. elapsedms += ( now.tv_usec - _command_sent_time.tv_usec ) / 1000.0;
  161. return elapsedms;
  162. }
  163. int pending_command ( void )
  164. {
  165. return _pending_command;
  166. }
  167. // capability should be enclosed in colons. I.e. ":switch:"
  168. bool
  169. is_capable_of ( const char *capability ) const
  170. {
  171. return capabilities &&
  172. strstr( capabilities, capability );
  173. }
  174. Client ( )
  175. {
  176. _label = 0;
  177. _gui_visible = true;
  178. addr = 0;
  179. _reply_errcode = 0;
  180. _reply_message = 0;
  181. pid = 0;
  182. progress = -0;
  183. _pending_command = 0;
  184. active = false;
  185. client_id = 0;
  186. capabilities = 0;
  187. name = 0;
  188. executable_path = 0;
  189. pre_existing = false;
  190. }
  191. ~Client ( )
  192. {
  193. if ( name )
  194. free(name);
  195. if (executable_path)
  196. free(executable_path);
  197. if (client_id)
  198. free(client_id);
  199. if (capabilities)
  200. free(capabilities);
  201. name = executable_path = client_id = capabilities = NULL;
  202. }
  203. };
  204. static std::list< Client* > client;
  205. /* helper macros for defining OSC handlers */
  206. #define OSC_NAME( name ) osc_ ## name
  207. // #define OSCDMSG() DMESSAGE( "Got OSC message: %s", path );
  208. #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 )
  209. static char *session_path = NULL;
  210. static char *session_name = NULL;
  211. bool
  212. clients_have_errors ( )
  213. {
  214. for ( std::list<Client*>::const_iterator i = client.begin();
  215. i != client.end();
  216. ++i )
  217. if ( (*i)->active && (*i)->has_error() )
  218. return true;
  219. return false;
  220. }
  221. Client *
  222. get_client_by_pid ( int pid )
  223. {
  224. std::list<Client*> *cl = &client;
  225. for ( std::list<Client*>::const_iterator i = cl->begin();
  226. i != cl->end();
  227. ++i )
  228. if ( (*i)->pid == pid )
  229. return *i;
  230. return NULL;
  231. }
  232. void clear_clients ( void )
  233. {
  234. std::list<Client*> *cl = &client;
  235. for ( std::list<Client*>::iterator i = cl->begin();
  236. i != cl->end();
  237. ++i )
  238. {
  239. delete *i;
  240. i = cl->erase( i );
  241. }
  242. }
  243. void
  244. handle_client_process_death ( int pid )
  245. {
  246. Client *c = get_client_by_pid( (int)pid );
  247. if ( c )
  248. {
  249. MESSAGE( "Client %s died.", c->name );
  250. bool dead_because_we_said = false;
  251. if ( c->pending_command() == COMMAND_KILL ||
  252. c->pending_command() == COMMAND_QUIT )
  253. {
  254. dead_because_we_said = true;
  255. }
  256. c->pending_command( COMMAND_NONE );
  257. c->active = false;
  258. c->pid = 0;
  259. if ( dead_because_we_said )
  260. {
  261. if ( gui_is_active )
  262. osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "removed" );
  263. client.remove( c );
  264. delete c;
  265. }
  266. else
  267. {
  268. if ( gui_is_active )
  269. osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "stopped" );
  270. }
  271. }
  272. }
  273. void handle_sigchld ( )
  274. {
  275. for ( ;; )
  276. {
  277. int status;
  278. pid_t pid = waitpid(-1, &status, WNOHANG);
  279. if (pid <= 0)
  280. break;
  281. handle_client_process_death( pid );
  282. }
  283. }
  284. int
  285. path_is_valid ( const char *path )
  286. {
  287. char *s;
  288. asprintf( &s, "/%s/", path );
  289. int r = strstr( s, "/../" ) == NULL;
  290. free( s );
  291. return r;
  292. }
  293. int
  294. mkpath ( const char *path, bool create_final_directory )
  295. {
  296. char *p = strdup( path );
  297. char *i = p + 1;
  298. while ( ( i = index( i, '/' ) ) )
  299. {
  300. *i = 0;
  301. struct stat st;
  302. if ( stat( p, &st ) )
  303. {
  304. if ( mkdir( p, 0711 ) )
  305. {
  306. free( p );
  307. return -1;
  308. }
  309. }
  310. *i = '/';
  311. i++;
  312. }
  313. if ( create_final_directory )
  314. {
  315. if ( mkdir( p, 0711 ) )
  316. {
  317. free( p );
  318. return -1;
  319. }
  320. }
  321. free( p );
  322. return 0;
  323. }
  324. void
  325. set_name ( const char *name )
  326. {
  327. if ( session_name )
  328. free( session_name );
  329. char *s = strdup( name );
  330. session_name = strdup( basename( s ) );
  331. free( s );
  332. }
  333. bool
  334. address_matches ( lo_address addr1, lo_address addr2 )
  335. {
  336. /* char *url1 = lo_address_get_url( addr1 ); */
  337. /* char *url2 = lo_address_get_url( addr2 ); */
  338. char *url1 = strdup( lo_address_get_port( addr1 ) );
  339. char *url2 = strdup(lo_address_get_port( addr2 ) );
  340. bool r = !strcmp( url1, url2 );
  341. free( url1 );
  342. free( url2 );
  343. return r;
  344. }
  345. Client *
  346. get_client_by_id ( std::list<Client*> *cl, const char *id )
  347. {
  348. for ( std::list<Client*>::const_iterator i = cl->begin();
  349. i != cl->end();
  350. ++i )
  351. if ( !strcmp( (*i)->client_id, id ) )
  352. return *i;
  353. return NULL;
  354. }
  355. Client *
  356. get_client_by_name_and_id ( std::list<Client*> *cl, const char *name, const char *id )
  357. {
  358. for ( std::list<Client*>::const_iterator i = cl->begin();
  359. i != cl->end();
  360. ++i )
  361. if ( !strcmp( (*i)->client_id, id ) &&
  362. ! strcmp( (*i)->name, name ) )
  363. return *i;
  364. return NULL;
  365. }
  366. Client *
  367. get_client_by_address ( lo_address addr )
  368. {
  369. for ( std::list<Client*>::iterator i = client.begin();
  370. i != client.end();
  371. ++i )
  372. if ( (*i)->addr && address_matches( (*i)->addr, addr ) )
  373. return *i;
  374. return NULL;
  375. }
  376. bool
  377. replies_still_pending ( )
  378. {
  379. for ( std::list<Client*>::const_iterator i = client.begin();
  380. i != client.end();
  381. ++i )
  382. /* if ( (*i)->active && (*i)->reply_pending() ) */
  383. /* return true; */
  384. if ( (*i)->reply_pending() )
  385. return true;
  386. return false;
  387. }
  388. char *
  389. generate_client_id ( Client *c )
  390. {
  391. char id_str[6];
  392. id_str[0] = 'n';
  393. id_str[5] = 0;
  394. for ( int i = 1; i < 5; i++)
  395. id_str[i] = 'A' + (rand() % 25);
  396. return strdup(id_str);
  397. }
  398. void
  399. wait_for_replies ( )
  400. {
  401. fprintf( stdout, "Waiting..." );
  402. fflush(stdout);
  403. int n = 7;
  404. while ( n-- )
  405. {
  406. printf( "." );
  407. fflush(stdout);
  408. osc_server->wait( 1000 );
  409. if ( ! replies_still_pending() )
  410. break;
  411. }
  412. /* FIXME: do something about unresponsive clients */
  413. }
  414. char *
  415. get_client_project_path ( const char *session_path, Client *c )
  416. {
  417. char *client_project_path;
  418. asprintf( &client_project_path, "%s/%s.%s", session_path, c->name, c->client_id );
  419. return client_project_path;
  420. }
  421. bool
  422. launch ( const char *executable, const char *client_id )
  423. {
  424. Client *c;
  425. if ( !client_id || !( c = get_client_by_id( &client, client_id ) ) )
  426. {
  427. c = new Client();
  428. c->executable_path = strdup( executable );
  429. {
  430. char *s = strdup( c->executable_path );
  431. c->name = strdup( basename( s ) );
  432. free( s );
  433. }
  434. if ( client_id )
  435. c->client_id = strdup( client_id );
  436. else
  437. c->client_id = generate_client_id( c );
  438. client.push_back( c );
  439. }
  440. char * url = osc_server->url();
  441. int pid;
  442. if ( ! (pid = fork()) )
  443. {
  444. MESSAGE( "Launching %s\n", executable );
  445. char *args[] = { strdup( executable ), NULL };
  446. setenv( "NSM_URL", url, 1 );
  447. if ( -1 == execvp( executable, args ) )
  448. {
  449. WARNING( "Error starting process: %s", strerror( errno ) );
  450. exit(-1);
  451. }
  452. }
  453. c->pending_command( COMMAND_START );
  454. c->pid = pid;
  455. MESSAGE( "Process has pid: %i", pid );
  456. if ( gui_is_active )
  457. {
  458. osc_server->send( gui_addr, "/nsm/gui/client/new", c->client_id, c->name );
  459. osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "launch" );
  460. }
  461. return true;
  462. }
  463. void
  464. command_client_to_save ( Client *c )
  465. {
  466. if ( c->active )
  467. {
  468. MESSAGE( "Telling %s to save", c->name );
  469. osc_server->send( c->addr, "/nsm/client/save" );
  470. c->pending_command( COMMAND_SAVE );
  471. if ( gui_is_active )
  472. osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "save" );
  473. }
  474. else if ( c->is_dumb_client() && c->pid )
  475. {
  476. // this is a dumb client...
  477. if ( gui_is_active )
  478. osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "noop" );
  479. }
  480. }
  481. void command_client_to_switch ( Client *c, const char *new_client_id )
  482. {
  483. char *old_client_id = c->client_id;
  484. c->client_id = strdup( new_client_id );
  485. char *client_project_path = get_client_project_path( session_path, c );
  486. MESSAGE( "Commanding %s to switch \"%s\"", c->name, client_project_path );
  487. char *full_client_id;
  488. asprintf( &full_client_id, "%s.%s", c->name, c->client_id );
  489. osc_server->send( c->addr, "/nsm/client/open", client_project_path, session_name, full_client_id );
  490. free( full_client_id );
  491. free( client_project_path );
  492. c->pending_command( COMMAND_OPEN );
  493. if ( gui_is_active )
  494. {
  495. osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "switch" );
  496. osc_server->send( gui_addr, "/nsm/gui/client/switch", old_client_id, c->client_id );
  497. }
  498. free( old_client_id );
  499. }
  500. void
  501. purge_inactive_clients ( )
  502. {
  503. for ( std::list<Client*>::iterator i = client.begin();
  504. i != client.end();
  505. ++i )
  506. {
  507. if ( ! (*i)->active )
  508. {
  509. if ( gui_is_active )
  510. osc_server->send( gui_addr, "/nsm/gui/client/status", (*i)->client_id, (*i)->status = "removed" );
  511. delete *i;
  512. i = client.erase( i );
  513. }
  514. }
  515. }
  516. bool
  517. process_is_running ( int pid )
  518. {
  519. if ( 0 == kill( pid, 0 ) )
  520. {
  521. return true;
  522. }
  523. else if ( ESRCH == errno )
  524. {
  525. return false;
  526. }
  527. return false;
  528. }
  529. void
  530. purge_dead_clients ( )
  531. {
  532. std::list<Client*> tmp( client );
  533. for ( std::list<Client*>::const_iterator i = tmp.begin();
  534. i != tmp.end();
  535. ++i )
  536. {
  537. const Client *c = *i;
  538. if ( c->pid )
  539. {
  540. if ( ! process_is_running( c->pid ) )
  541. handle_client_process_death( c->pid );
  542. }
  543. }
  544. }
  545. /************************/
  546. /* OSC Message Handlers */
  547. /************************/
  548. OSC_HANDLER( add )
  549. {
  550. if ( ! session_path )
  551. {
  552. osc_server->send( lo_message_get_source( msg ), path,
  553. ERR_NO_SESSION_OPEN,
  554. "Cannot add to session because no session is loaded." );
  555. return 0;
  556. }
  557. if ( ! launch( &argv[0]->s, NULL ) )
  558. {
  559. osc_server->send( lo_message_get_source( msg ), path,
  560. ERR_LAUNCH_FAILED,
  561. "Failed to launch process!" );
  562. }
  563. else
  564. {
  565. osc_server->send( lo_message_get_source( msg ), path,
  566. ERR_OK,
  567. "Launched." );
  568. }
  569. return 0;
  570. }
  571. OSC_HANDLER( announce )
  572. {
  573. MESSAGE( "Got announce" );
  574. const char *client_name = &argv[0]->s;
  575. const char *capabilities = &argv[1]->s;
  576. const char *executable_path = &argv[2]->s;
  577. int major = argv[3]->i;
  578. int minor = argv[4]->i;
  579. int pid = argv[5]->i;
  580. if ( ! session_path )
  581. {
  582. osc_server->send( lo_message_get_source( msg ), "/error",
  583. path,
  584. ERR_NO_SESSION_OPEN,
  585. "Sorry, but there's no session open for this application to join." );
  586. return 0;
  587. }
  588. bool expected_client = false;
  589. Client *c = NULL;
  590. for ( std::list<Client*>::iterator i = client.begin();
  591. i != client.end();
  592. ++i )
  593. {
  594. if ( ! strcmp( (*i)->executable_path, executable_path ) &&
  595. ! (*i)->active &&
  596. (*i)->pending_command() == COMMAND_START )
  597. {
  598. // I think we've found the slot we were looking for.
  599. MESSAGE( "Client was expected." );
  600. c = *i;
  601. break;
  602. }
  603. }
  604. if ( ! c )
  605. {
  606. c = new Client();
  607. c->executable_path = strdup( executable_path );
  608. c->client_id = generate_client_id( c );
  609. }
  610. else
  611. expected_client = true;
  612. if ( major > NSM_API_VERSION_MAJOR )
  613. {
  614. MESSAGE( "Client is using incompatible and more recent API version %i.%i", major, minor );
  615. osc_server->send( lo_message_get_source( msg ), "/error",
  616. path,
  617. ERR_INCOMPATIBLE_API,
  618. "Server is using an incompatible API version." );
  619. return 0;
  620. }
  621. c->pid = pid;
  622. c->capabilities = strdup( capabilities );
  623. c->addr = lo_address_new_from_url( lo_address_get_url( lo_message_get_source( msg ) ));
  624. c->name = strdup( client_name );
  625. c->active = true;
  626. MESSAGE( "Process has pid: %i", pid );
  627. if ( ! expected_client )
  628. client.push_back( c );
  629. MESSAGE( "The client \"%s\" at \"%s\" informs us it's ready to receive commands.", &argv[0]->s, lo_address_get_url( c->addr ) );
  630. osc_server->send( lo_message_get_source( msg ), "/reply",
  631. path,
  632. expected_client ?
  633. "Howdy, what took you so long?" :
  634. "Well hello, stranger. Welcome to the party.",
  635. APP_TITLE,
  636. ":server-control:broadcast:" );
  637. if ( gui_is_active )
  638. {
  639. osc_server->send( gui_addr, "/nsm/gui/client/new", c->client_id, c->name );
  640. osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "open" );
  641. if ( c->is_capable_of( ":optional-gui:" ) )
  642. osc_server->send( gui_addr, "/nsm/gui/client/has_optional_gui", c->client_id );
  643. }
  644. {
  645. char *full_client_id;
  646. asprintf( &full_client_id, "%s.%s", c->name, c->client_id );
  647. char *client_project_path = get_client_project_path( session_path, c );
  648. osc_server->send( lo_message_get_source( msg ), "/nsm/client/open", client_project_path, session_name, full_client_id );
  649. c->pending_command( COMMAND_OPEN );
  650. free( full_client_id );
  651. free( client_project_path );
  652. }
  653. return 0;
  654. }
  655. void
  656. save_session_file ( )
  657. {
  658. char *session_file = NULL;
  659. asprintf( &session_file, "%s/session.nsm", session_path );
  660. FILE *fp = fopen( session_file, "w+" );
  661. /* FIXME: handle errors. */
  662. for ( std::list<Client*>::iterator i = client.begin();
  663. i != client.end();
  664. ++i )
  665. {
  666. fprintf( fp, "%s:%s:%s\n", (*i)->name, (*i)->executable_path, (*i)->client_id );
  667. }
  668. fclose( fp );
  669. }
  670. Client *
  671. client_by_name ( const char *name,
  672. std::list<Client*> *cl )
  673. {
  674. for ( std::list<Client*>::iterator i = cl->begin();
  675. i != cl->end();
  676. ++i )
  677. {
  678. if ( !strcmp( name, (*i)->name ) )
  679. return *i;
  680. }
  681. return NULL;
  682. }
  683. bool
  684. dumb_clients_are_alive ( )
  685. {
  686. std::list<Client*> *cl = &client;
  687. for ( std::list<Client*>::iterator i = cl->begin();
  688. i != cl->end();
  689. ++i )
  690. {
  691. if ( (*i)->is_dumb_client() && (*i)->pid > 0 )
  692. return true;
  693. }
  694. return false;
  695. }
  696. void
  697. wait_for_dumb_clients_to_die ( )
  698. {
  699. struct signalfd_siginfo fdsi;
  700. MESSAGE( "Waiting for any dumb clients to die." );
  701. for ( int i = 0; i < 6; i++ )
  702. {
  703. MESSAGE( "Loop %i", i );
  704. if ( ! dumb_clients_are_alive() )
  705. break;
  706. ssize_t s = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo));
  707. if (s == sizeof(struct signalfd_siginfo))
  708. {
  709. if (fdsi.ssi_signo == SIGCHLD)
  710. handle_sigchld();
  711. }
  712. usleep( 50000 );
  713. }
  714. /* FIXME: give up on remaining clients and purge them */
  715. }
  716. bool
  717. killed_clients_are_alive ( )
  718. {
  719. std::list<Client*> *cl = &client;
  720. for ( std::list<Client*>::iterator i = cl->begin();
  721. i != cl->end();
  722. ++i )
  723. {
  724. if ( ( (*i)->pending_command() == COMMAND_QUIT ||
  725. (*i)->pending_command() == COMMAND_KILL ) &&
  726. (*i)->pid > 0 )
  727. return true;
  728. }
  729. return false;
  730. }
  731. void
  732. wait_for_killed_clients_to_die ( )
  733. {
  734. struct signalfd_siginfo fdsi;
  735. MESSAGE( "Waiting for killed clients to die." );
  736. for ( int i = 0; i < 24; i++ )
  737. {
  738. MESSAGE( "Loop %i", i );
  739. if ( ! killed_clients_are_alive() )
  740. break;
  741. ssize_t s = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo));
  742. if (s == sizeof(struct signalfd_siginfo))
  743. {
  744. if (fdsi.ssi_signo == SIGCHLD)
  745. handle_sigchld();
  746. }
  747. purge_dead_clients();
  748. usleep( 200 * 1000 );
  749. }
  750. if ( killed_clients_are_alive() )
  751. {
  752. WARNING( "Killed clients are still alive" );
  753. /* FIXME: give up on remaining clients and purge them */
  754. }
  755. else
  756. MESSAGE( "All clients have died." );
  757. }
  758. void
  759. command_all_clients_to_save ( )
  760. {
  761. if ( session_path )
  762. {
  763. MESSAGE( "Commanding attached clients to save." );
  764. for ( std::list<Client*>::iterator i = client.begin();
  765. i != client.end();
  766. ++i )
  767. {
  768. command_client_to_save( *i );
  769. }
  770. wait_for_replies();
  771. save_session_file();
  772. }
  773. }
  774. void
  775. command_client_to_quit ( Client *c )
  776. {
  777. MESSAGE( "Commanding %s to quit", c->name );
  778. if ( c->active )
  779. {
  780. c->pending_command( COMMAND_QUIT );
  781. kill( c->pid, SIGTERM );
  782. if ( gui_is_active )
  783. osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "quit" );
  784. }
  785. else if ( c->is_dumb_client() )
  786. {
  787. if ( c->pid > 0 )
  788. {
  789. if ( gui_is_active )
  790. osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "kill" );
  791. /* should be kill? */
  792. c->pending_command( COMMAND_KILL );
  793. // this is a dumb client... try and kill it
  794. kill( c->pid, SIGTERM );
  795. }
  796. else
  797. {
  798. if ( gui_is_active )
  799. osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "removed" );
  800. }
  801. }
  802. }
  803. void
  804. close_session ( )
  805. {
  806. if ( ! session_path )
  807. return;
  808. for ( std::list<Client*>::iterator i = client.begin();
  809. i != client.end();
  810. ++i )
  811. {
  812. command_client_to_quit( *i );
  813. }
  814. wait_for_killed_clients_to_die();
  815. purge_inactive_clients();
  816. clear_clients();
  817. if ( session_path )
  818. {
  819. char *session_lock;
  820. asprintf( &session_lock, "%s/.lock", session_path );
  821. release_lock( &session_lock_fd, session_lock );
  822. free(session_lock);
  823. free(session_path);
  824. session_path = NULL;
  825. free(session_name);
  826. session_name = NULL;
  827. }
  828. if ( gui_is_active )
  829. {
  830. osc_server->send( gui_addr, "/nsm/gui/session/name", "" );
  831. }
  832. }
  833. void
  834. tell_client_session_is_loaded( Client *c )
  835. {
  836. if ( c->active )
  837. //!c->is_dumb_client() )
  838. {
  839. MESSAGE( "Telling client %s that session is loaded.", c->name );
  840. osc_server->send( c->addr, "/nsm/client/session_is_loaded" );
  841. }
  842. }
  843. void
  844. tell_all_clients_session_is_loaded ( void )
  845. {
  846. MESSAGE( "Telling all clients that session is loaded..." );
  847. for ( std::list<Client*>::iterator i = client.begin();
  848. i != client.end();
  849. ++i )
  850. {
  851. tell_client_session_is_loaded( *i );
  852. }
  853. }
  854. int
  855. load_session_file ( const char * path )
  856. {
  857. char *session_file = NULL;
  858. asprintf( &session_file, "%s/session.nsm", path );
  859. char *session_lock = NULL;
  860. asprintf( &session_lock, "%s/.lock", path );
  861. if ( ! acquire_lock( &session_lock_fd, session_lock ) )
  862. {
  863. free( session_file );
  864. free( session_lock );
  865. WARNING( "Session is locked by another process" );
  866. return ERR_SESSION_LOCKED;
  867. }
  868. FILE *fp;
  869. if ( ! ( fp = fopen( session_file, "r" ) ) )
  870. {
  871. free( session_file );
  872. return ERR_CREATE_FAILED;
  873. }
  874. session_path = strdup( path );
  875. set_name( path );
  876. std::list<Client*> new_clients;
  877. {
  878. char * client_name = NULL;
  879. char * client_executable = NULL;
  880. char * client_id = NULL;
  881. // load the client list
  882. while ( fscanf( fp, "%a[^:]:%a[^:]:%a[^:\n]\n", &client_name, &client_executable, &client_id ) > 0 )
  883. {
  884. Client *c = new Client();
  885. c->name = client_name;
  886. c->executable_path = client_executable;
  887. c->client_id = client_id;
  888. new_clients.push_back( c );
  889. }
  890. }
  891. MESSAGE( "Commanding unneeded and dumb clients to quit" );
  892. for ( std::list<Client*>::iterator i = client.begin();
  893. i != client.end();
  894. ++i )
  895. {
  896. if ( ! (*i)->is_capable_of( ":switch:" )
  897. ||
  898. ! client_by_name( (*i)->name, &new_clients ) )
  899. {
  900. command_client_to_quit( *i );
  901. }
  902. }
  903. // wait_for_replies();
  904. wait_for_killed_clients_to_die();
  905. // wait_for_dumb_clients_to_die();
  906. purge_inactive_clients();
  907. for ( std::list<Client*>::iterator i = client.begin();
  908. i != client.end();
  909. ++i )
  910. {
  911. (*i)->pre_existing = true;
  912. }
  913. MESSAGE( "Commanding smart clients to switch" );
  914. for ( std::list<Client*>::iterator i = new_clients.begin();
  915. i != new_clients.end();
  916. ++i )
  917. {
  918. Client *c = NULL;
  919. /* in a duplicated session, clients will have the same
  920. * IDs, so be sure to pick the right one to avoid race
  921. * conditions in JACK name registration. */
  922. c = get_client_by_name_and_id( &client, (*i)->name, (*i)->client_id );
  923. if ( ! c )
  924. c = client_by_name( (*i)->name, &client );
  925. if ( c && c->pre_existing && !c->reply_pending() )
  926. {
  927. // since we already shutdown clients not capable of 'switch', we can assume that these are.
  928. command_client_to_switch( c, (*i)->client_id );
  929. }
  930. else
  931. {
  932. /* sleep a little bit because liblo derives its sequence
  933. * of port numbers from the system time (second
  934. * resolution) and if too many clients start at once they
  935. * won't be able to find a free port. */
  936. usleep( 100 * 1000 );
  937. launch( (*i)->executable_path, (*i)->client_id );
  938. }
  939. }
  940. /* this part is a little tricky... the clients need some time to
  941. * send their 'announce' messages before we can send them 'open'
  942. * and know that a reply is pending and we should continue waiting
  943. * until they finish. wait_for_replies() must check for OSC
  944. * messages immediately, even if no replies seem to be pending
  945. * yet. */
  946. // osc_server->wait( 3000 );
  947. wait_for_replies();
  948. tell_all_clients_session_is_loaded();
  949. MESSAGE( "Loaded." );
  950. new_clients.clear();
  951. if ( gui_is_active )
  952. {
  953. osc_server->send( gui_addr, "/nsm/gui/session/name", session_name );
  954. }
  955. return ERR_OK;
  956. }
  957. OSC_HANDLER( save )
  958. {
  959. if ( pending_operation != COMMAND_NONE )
  960. {
  961. osc_server->send( lo_message_get_source( msg ), "/error", path,
  962. ERR_OPERATION_PENDING,
  963. "An operation pending." );
  964. return 0;
  965. }
  966. if ( ! session_path )
  967. {
  968. osc_server->send( lo_message_get_source( msg ), "/error", path,
  969. ERR_NO_SESSION_OPEN,
  970. "No session to save.");
  971. goto done;
  972. }
  973. command_all_clients_to_save();
  974. MESSAGE( "Done." );
  975. osc_server->send( lo_message_get_source( msg ), "/reply", path, "Saved." );
  976. done:
  977. pending_operation = COMMAND_NONE;
  978. return 0;
  979. }
  980. OSC_HANDLER( duplicate )
  981. {
  982. if ( pending_operation != COMMAND_NONE )
  983. {
  984. osc_server->send( lo_message_get_source( msg ), "/error", path,
  985. ERR_OPERATION_PENDING,
  986. "An operation pending." );
  987. return 0;
  988. }
  989. pending_operation = COMMAND_DUPLICATE;
  990. if ( ! session_path )
  991. {
  992. osc_server->send( lo_message_get_source( msg ), "/error", path,
  993. ERR_NO_SESSION_OPEN,
  994. "No session to duplicate.");
  995. goto done;
  996. }
  997. if ( ! path_is_valid( &argv[0]->s ) )
  998. {
  999. osc_server->send( lo_message_get_source( msg ), "/error", path,
  1000. ERR_CREATE_FAILED,
  1001. "Invalid session name." );
  1002. goto done;
  1003. }
  1004. command_all_clients_to_save();
  1005. if ( clients_have_errors() )
  1006. {
  1007. osc_server->send( lo_message_get_source( msg ), "/error", path,
  1008. ERR_GENERAL_ERROR,
  1009. "Some clients could not save" );
  1010. goto done;
  1011. }
  1012. // save_session_file();
  1013. char *spath;
  1014. asprintf( &spath, "%s/%s", session_root, &argv[0]->s );
  1015. mkpath( spath, false );
  1016. /* FIXME: code a recursive copy instead of calling the shell */
  1017. char *cmd;
  1018. asprintf( &cmd, "cp -R \"%s\" \"%s\"", session_path, spath);
  1019. system( cmd );
  1020. free( cmd );
  1021. osc_server->send( gui_addr, "/nsm/gui/session/session", &argv[0]->s );
  1022. MESSAGE( "Attempting to open %s", spath );
  1023. if ( load_session_file( spath ) )
  1024. {
  1025. MESSAGE( "Loaded" );
  1026. osc_server->send( lo_message_get_source( msg ), "/reply", path,
  1027. "Loaded." );
  1028. }
  1029. else
  1030. {
  1031. MESSAGE( "Failed" );
  1032. osc_server->send( lo_message_get_source( msg ), "/error", path,
  1033. ERR_NO_SUCH_FILE,
  1034. "No such file." );
  1035. }
  1036. free( spath );
  1037. MESSAGE( "Done" );
  1038. osc_server->send( lo_message_get_source( msg ), "/reply", path, "Duplicated." );
  1039. done:
  1040. pending_operation = COMMAND_NONE;
  1041. return 0;
  1042. }
  1043. OSC_HANDLER( new )
  1044. {
  1045. if ( pending_operation != COMMAND_NONE )
  1046. {
  1047. osc_server->send( lo_message_get_source( msg ), "/error", path,
  1048. ERR_OPERATION_PENDING,
  1049. "An operation pending." );
  1050. return 0;
  1051. }
  1052. pending_operation = COMMAND_NEW;
  1053. if ( ! path_is_valid( &argv[0]->s ) )
  1054. {
  1055. osc_server->send( lo_message_get_source( msg ), "/error", path,
  1056. ERR_CREATE_FAILED,
  1057. "Invalid session name." );
  1058. pending_operation = COMMAND_NONE;
  1059. return 0;
  1060. }
  1061. if ( session_path )
  1062. {
  1063. command_all_clients_to_save();
  1064. close_session();
  1065. }
  1066. MESSAGE( "Creating new session" );
  1067. char *spath;
  1068. asprintf( &spath, "%s/%s", session_root, &argv[0]->s );
  1069. if ( mkpath( spath, true ) )
  1070. {
  1071. osc_server->send( lo_message_get_source( msg ), "/error", path,
  1072. ERR_CREATE_FAILED,
  1073. "Could not create the session directory" );
  1074. free(spath);
  1075. pending_operation = COMMAND_NONE;
  1076. return 0;
  1077. }
  1078. session_path = strdup( spath );
  1079. set_name( session_path );
  1080. osc_server->send( lo_message_get_source( msg ), "/reply", path, "Created." );
  1081. if ( gui_is_active )
  1082. {
  1083. osc_server->send( gui_addr, "/nsm/gui/session/session", &argv[0]->s );
  1084. osc_server->send( gui_addr, "/nsm/gui/session/name", &argv[0]->s );
  1085. }
  1086. save_session_file();
  1087. free( spath );
  1088. osc_server->send( lo_message_get_source( msg ), "/reply", path,
  1089. "Session created" );
  1090. pending_operation = COMMAND_NONE;
  1091. return 0;
  1092. }
  1093. static lo_address list_response_address;
  1094. int
  1095. list_file ( const char *fpath, const struct stat *sb, int tflag )
  1096. {
  1097. char *s;
  1098. if ( tflag == FTW_F )
  1099. {
  1100. s = strdup( fpath );
  1101. if ( ! strcmp( "session.nsm", basename( s ) ) )
  1102. {
  1103. free( s );
  1104. s = strdup( fpath );
  1105. s = dirname( s );
  1106. memmove( s, s + strlen( session_root ) + 1, (strlen( s ) - strlen( session_root )) + 1);
  1107. osc_server->send( list_response_address, "/reply", "/nsm/server/list", s );
  1108. free( s );
  1109. }
  1110. else
  1111. free( s );
  1112. }
  1113. return 0;
  1114. }
  1115. OSC_HANDLER( list )
  1116. {
  1117. MESSAGE( "Listing sessions" );
  1118. list_response_address = lo_message_get_source( msg );
  1119. ftw( session_root, list_file, 20 );
  1120. osc_server->send( lo_message_get_source( msg ), path,
  1121. ERR_OK,
  1122. "Done." );
  1123. return 0;
  1124. }
  1125. OSC_HANDLER( open )
  1126. {
  1127. DMESSAGE( "Got open" );
  1128. if ( pending_operation != COMMAND_NONE )
  1129. {
  1130. osc_server->send( lo_message_get_source( msg ), "/error", path,
  1131. ERR_OPERATION_PENDING,
  1132. "An operation pending." );
  1133. return 0;
  1134. }
  1135. pending_operation = COMMAND_OPEN;
  1136. if ( session_path )
  1137. {
  1138. command_all_clients_to_save();
  1139. if ( clients_have_errors() )
  1140. {
  1141. osc_server->send( lo_message_get_source( msg ), "/error", path,
  1142. ERR_GENERAL_ERROR,
  1143. "Some clients could not save" );
  1144. pending_operation = COMMAND_NONE;
  1145. return 0;
  1146. }
  1147. // save_session_file();
  1148. }
  1149. char *spath;
  1150. asprintf( &spath, "%s/%s", session_root, &argv[0]->s );
  1151. MESSAGE( "Attempting to open %s", spath );
  1152. int err = load_session_file( spath );
  1153. if ( ! err )
  1154. {
  1155. MESSAGE( "Loaded" );
  1156. osc_server->send( lo_message_get_source( msg ), "/reply", path,
  1157. "Loaded." );
  1158. }
  1159. else
  1160. {
  1161. MESSAGE( "Failed" );
  1162. const char *m = NULL;
  1163. switch ( err )
  1164. {
  1165. case ERR_CREATE_FAILED:
  1166. m = "Could not create session file!";
  1167. break;
  1168. case ERR_SESSION_LOCKED:
  1169. m = "Session is locked by another process!";
  1170. break;
  1171. case ERR_NO_SUCH_FILE:
  1172. m = "The named session does not exist.";
  1173. break;
  1174. default:
  1175. m = "Unknown error";
  1176. }
  1177. osc_server->send( lo_message_get_source( msg ), "/error", path,
  1178. err,
  1179. m );
  1180. }
  1181. free( spath );
  1182. MESSAGE( "Done" );
  1183. pending_operation = COMMAND_NONE;
  1184. return 0;
  1185. }
  1186. OSC_HANDLER( quit )
  1187. {
  1188. close_session();
  1189. exit(0);
  1190. return 0;
  1191. }
  1192. OSC_HANDLER( abort )
  1193. {
  1194. if ( pending_operation != COMMAND_NONE )
  1195. {
  1196. osc_server->send( lo_message_get_source( msg ), "/error", path,
  1197. ERR_OPERATION_PENDING,
  1198. "An operation pending." );
  1199. return 0;
  1200. }
  1201. pending_operation = COMMAND_CLOSE;
  1202. if ( ! session_path )
  1203. {
  1204. osc_server->send( lo_message_get_source( msg ), "/error", path,
  1205. ERR_NO_SESSION_OPEN,
  1206. "No session to abort." );
  1207. goto done;
  1208. }
  1209. MESSAGE( "Commanding attached clients to quit." );
  1210. close_session();
  1211. osc_server->send( lo_message_get_source( msg ), "/reply", path,
  1212. "Aborted." );
  1213. MESSAGE( "Done" );
  1214. done:
  1215. pending_operation = COMMAND_NONE;
  1216. return 0;
  1217. }
  1218. OSC_HANDLER( close )
  1219. {
  1220. if ( pending_operation != COMMAND_NONE )
  1221. {
  1222. osc_server->send( lo_message_get_source( msg ), "/error", path,
  1223. ERR_OPERATION_PENDING,
  1224. "An operation pending." );
  1225. return 0;
  1226. }
  1227. pending_operation = COMMAND_CLOSE;
  1228. if ( ! session_path )
  1229. {
  1230. osc_server->send( lo_message_get_source( msg ), "/error", path,
  1231. ERR_NO_SESSION_OPEN,
  1232. "No session to close." );
  1233. goto done;
  1234. }
  1235. command_all_clients_to_save();
  1236. MESSAGE( "Commanding attached clients to quit." );
  1237. close_session();
  1238. osc_server->send( lo_message_get_source( msg ), "/reply", path,
  1239. "Closed." );
  1240. MESSAGE( "Done" );
  1241. done:
  1242. pending_operation = COMMAND_NONE;
  1243. return 0;
  1244. }
  1245. OSC_HANDLER( broadcast )
  1246. {
  1247. const char *to_path = &argv[0]->s;
  1248. /* don't allow clients to broadcast NSM commands */
  1249. if ( ! strncmp( to_path, "/nsm/", strlen( "/nsm/" ) ) )
  1250. return 0;
  1251. std::list<OSC::OSC_Value> new_args;
  1252. for ( int i = 1; i < argc; ++i )
  1253. {
  1254. switch ( types[i] )
  1255. {
  1256. case 's':
  1257. new_args.push_back( OSC::OSC_String( &argv[i]->s ) );
  1258. break;
  1259. case 'i':
  1260. new_args.push_back( OSC::OSC_Int( argv[i]->i ) );
  1261. break;
  1262. case 'f':
  1263. new_args.push_back( OSC::OSC_Float( argv[i]->f ) );
  1264. break;
  1265. }
  1266. }
  1267. char *sender_url = lo_address_get_url( lo_message_get_source( msg ) );
  1268. for ( std::list<Client*>::iterator i = client.begin();
  1269. i != client.end();
  1270. ++i )
  1271. {
  1272. if ( ! (*i)->addr )
  1273. continue;
  1274. char *url = lo_address_get_url( (*i)->addr );
  1275. if ( strcmp( sender_url, url ) )
  1276. {
  1277. osc_server->send( (*i)->addr, to_path, new_args );
  1278. }
  1279. free( url );
  1280. }
  1281. /* also relay to attached GUI so that the broadcast can be
  1282. * propagated to another NSMD instance */
  1283. if ( gui_is_active )
  1284. {
  1285. char *u1 = lo_address_get_url( gui_addr );
  1286. if ( strcmp( u1, sender_url ) )
  1287. {
  1288. new_args.push_front( OSC::OSC_String( to_path ) );
  1289. osc_server->send( gui_addr, path, new_args );
  1290. }
  1291. free(u1);
  1292. }
  1293. free( sender_url );
  1294. return 0;
  1295. }
  1296. /*********************************/
  1297. /* Client Informational Messages */
  1298. /*********************************/
  1299. OSC_HANDLER( progress )
  1300. {
  1301. Client *c = get_client_by_address( lo_message_get_source( msg ) );
  1302. if ( c )
  1303. {
  1304. c->progress = argv[0]->f;
  1305. MESSAGE( "%s progress: %i%%", c->name, (int)(c->progress * 100.0f) );
  1306. if ( gui_is_active )
  1307. {
  1308. osc_server->send( gui_addr, "/nsm/gui/client/progress", c->client_id, (float)c->progress );
  1309. }
  1310. }
  1311. return 0;
  1312. }
  1313. OSC_HANDLER( is_dirty )
  1314. {
  1315. MESSAGE( "Client sends dirty" );
  1316. Client *c = get_client_by_address( lo_message_get_source( msg ) );
  1317. if ( ! c )
  1318. return 0;
  1319. c->dirty = 1;
  1320. if ( gui_is_active )
  1321. osc_server->send( gui_addr, "/nsm/gui/client/dirty", c->client_id, c->dirty );
  1322. return 0;
  1323. }
  1324. OSC_HANDLER( is_clean )
  1325. {
  1326. MESSAGE( "Client sends clean" );
  1327. Client *c = get_client_by_address( lo_message_get_source( msg ) );
  1328. if ( ! c )
  1329. return 0;
  1330. c->dirty = 0;
  1331. if ( gui_is_active )
  1332. osc_server->send( gui_addr, "/nsm/gui/client/dirty", c->client_id, c->dirty );
  1333. return 0;
  1334. }
  1335. OSC_HANDLER( gui_is_hidden )
  1336. {
  1337. MESSAGE( "Client sends gui hidden" );
  1338. Client *c = get_client_by_address( lo_message_get_source( msg ) );
  1339. if ( ! c )
  1340. return 0;
  1341. c->gui_visible( false );
  1342. if ( gui_is_active )
  1343. osc_server->send( gui_addr, "/nsm/gui/client/gui_visible", c->client_id, c->gui_visible() );
  1344. return 0;
  1345. }
  1346. OSC_HANDLER( gui_is_shown )
  1347. {
  1348. MESSAGE( "Client sends gui shown" );
  1349. Client *c = get_client_by_address( lo_message_get_source( msg ) );
  1350. if ( ! c )
  1351. return 0;
  1352. c->gui_visible( true );
  1353. if ( gui_is_active )
  1354. osc_server->send( gui_addr, "/nsm/gui/client/gui_visible", c->client_id, c->gui_visible() );
  1355. return 0;
  1356. }
  1357. OSC_HANDLER( message )
  1358. {
  1359. Client *c = get_client_by_address( lo_message_get_source( msg ) );
  1360. if ( ! c )
  1361. return 0;
  1362. if ( gui_is_active )
  1363. osc_server->send( gui_addr, "/nsm/gui/client/message", c->client_id, argv[0]->i, &argv[1]->s );
  1364. return 0;
  1365. }
  1366. OSC_HANDLER( label )
  1367. {
  1368. Client *c = get_client_by_address( lo_message_get_source( msg ) );
  1369. if ( ! c )
  1370. return 0;
  1371. if ( strcmp( types, "s" ) )
  1372. return -1;
  1373. c->label( &argv[0]->s );
  1374. if ( gui_is_active )
  1375. osc_server->send( gui_addr, "/nsm/gui/client/label", c->client_id, &argv[0]->s );
  1376. return 0;
  1377. }
  1378. /**********************/
  1379. /* Response Handlers */
  1380. /**********************/
  1381. OSC_HANDLER( error )
  1382. {
  1383. Client *c = get_client_by_address( lo_message_get_source( msg ) );
  1384. if ( ! c )
  1385. {
  1386. WARNING( "Error from unknown client" );
  1387. return 0;
  1388. }
  1389. // const char *rpath = &argv[0]->s;
  1390. int err_code = argv[1]->i;
  1391. const char *message = &argv[2]->s;
  1392. c->set_reply( err_code, message );
  1393. MESSAGE( "Client \"%s\" replied with error: %s (%i) in %fms", c->name, message, err_code, c->milliseconds_since_last_command() );
  1394. c->pending_command( COMMAND_NONE );
  1395. if ( gui_is_active )
  1396. osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "error" );
  1397. return 0;
  1398. }
  1399. OSC_HANDLER( reply )
  1400. {
  1401. Client *c = get_client_by_address( lo_message_get_source( msg ) );
  1402. // const char *rpath = &argv[0]->s;
  1403. const char *message = &argv[1]->s;
  1404. if ( c )
  1405. {
  1406. c->set_reply( ERR_OK, message );
  1407. MESSAGE( "Client \"%s\" replied with: %s in %fms", c->name, message, c->milliseconds_since_last_command() );
  1408. c->pending_command( COMMAND_NONE );
  1409. if ( gui_is_active )
  1410. osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "ready" );
  1411. }
  1412. else
  1413. MESSAGE( "Reply from unknown client" );
  1414. return 0;
  1415. }
  1416. /******************/
  1417. /* GUI operations */
  1418. /******************/
  1419. OSC_HANDLER( stop )
  1420. {
  1421. Client *c = get_client_by_id( &client, &argv[0]->s );
  1422. if ( c )
  1423. {
  1424. if ( c->pid != 0 )
  1425. {
  1426. kill( c->pid, SIGTERM );
  1427. if ( gui_is_active )
  1428. osc_server->send( gui_addr, "/reply", "Client stopped." );
  1429. }
  1430. }
  1431. else
  1432. {
  1433. if ( gui_is_active )
  1434. osc_server->send( gui_addr, "/error", -10, "No such client." );
  1435. }
  1436. return 0;
  1437. }
  1438. OSC_HANDLER( remove )
  1439. {
  1440. Client *c = get_client_by_id( &client, &argv[0]->s );
  1441. if ( c )
  1442. {
  1443. if ( c->pid == 0 &&
  1444. ! c->active )
  1445. {
  1446. osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status = "removed" );
  1447. client.remove( c );
  1448. delete c;
  1449. if ( gui_is_active )
  1450. osc_server->send( gui_addr, "/reply", "Client removed." );
  1451. }
  1452. }
  1453. else
  1454. {
  1455. if ( gui_is_active )
  1456. osc_server->send( gui_addr, "/error", -10, "No such client." );
  1457. }
  1458. return 0;
  1459. }
  1460. OSC_HANDLER( resume )
  1461. {
  1462. Client *c = get_client_by_id( &client, &argv[0]->s );
  1463. /* FIXME: return error if no such client? */
  1464. if ( c )
  1465. {
  1466. if ( c->pid == 0 &&
  1467. ! c->active )
  1468. {
  1469. if ( ! launch( c->executable_path, c->client_id ) )
  1470. {
  1471. }
  1472. }
  1473. }
  1474. return 0;
  1475. }
  1476. OSC_HANDLER( client_save )
  1477. {
  1478. Client *c = get_client_by_id( &client, &argv[0]->s );
  1479. /* FIXME: return error if no such client? */
  1480. if ( c )
  1481. {
  1482. if ( c->active )
  1483. {
  1484. command_client_to_save( c );
  1485. }
  1486. }
  1487. return 0;
  1488. }
  1489. OSC_HANDLER( client_show_optional_gui )
  1490. {
  1491. Client *c = get_client_by_id( &client, &argv[0]->s );
  1492. /* FIXME: return error if no such client? */
  1493. if ( c )
  1494. {
  1495. if ( c->active )
  1496. {
  1497. osc_server->send( c->addr, "/nsm/client/show_optional_gui" );
  1498. }
  1499. }
  1500. return 0;
  1501. }
  1502. OSC_HANDLER( client_hide_optional_gui )
  1503. {
  1504. Client *c = get_client_by_id( &client, &argv[0]->s );
  1505. /* FIXME: return error if no such client? */
  1506. if ( c )
  1507. {
  1508. if ( c->active )
  1509. {
  1510. osc_server->send( c->addr, "/nsm/client/hide_optional_gui" );
  1511. }
  1512. }
  1513. return 0;
  1514. }
  1515. void
  1516. announce_gui( const char *url, bool is_reply )
  1517. {
  1518. gui_addr = lo_address_new_from_url( url );
  1519. gui_is_active = true;
  1520. if ( is_reply )
  1521. osc_server->send( gui_addr, "/nsm/gui/gui_announce", "hi" );
  1522. else
  1523. osc_server->send( gui_addr, "/nsm/gui/server_announce", "hi" );
  1524. for ( std::list<Client*>::iterator i = client.begin();
  1525. i != client.end();
  1526. ++i )
  1527. {
  1528. Client *c = *i;
  1529. osc_server->send( gui_addr, "/nsm/gui/client/new", c->client_id, c->name );
  1530. osc_server->send( gui_addr, "/nsm/gui/client/status", c->client_id, c->status );
  1531. }
  1532. osc_server->send( gui_addr, "/nsm/gui/session/name", session_name ? session_name : "" );
  1533. DMESSAGE( "Registered with GUI" );
  1534. }
  1535. OSC_HANDLER( gui_announce )
  1536. {
  1537. announce_gui( lo_address_get_url( lo_message_get_source( msg ) ), true );
  1538. return 0;
  1539. }
  1540. OSC_HANDLER( ping )
  1541. {
  1542. osc_server->send( lo_message_get_source( msg ), "/reply", path );
  1543. return 0;
  1544. }
  1545. int main(int argc, char *argv[])
  1546. {
  1547. sigset_t mask;
  1548. sigemptyset( &mask );
  1549. sigaddset( &mask, SIGCHLD );
  1550. sigprocmask(SIG_BLOCK, &mask, NULL );
  1551. signal_fd = signalfd( -1, &mask, SFD_NONBLOCK );
  1552. /* generate random seed for client ids */
  1553. {
  1554. time_t seconds;
  1555. time(&seconds);
  1556. srand( (unsigned int) seconds );
  1557. }
  1558. // char *osc_port = "6666";
  1559. char *osc_port = NULL;
  1560. const char *gui_url = NULL;
  1561. static struct option long_options[] =
  1562. {
  1563. { "detach", no_argument, 0, 'd' },
  1564. { "session-root", required_argument, 0, 's' },
  1565. { "osc-port", required_argument, 0, 'p' },
  1566. { "gui-url", required_argument, 0, 'g' },
  1567. { "help", no_argument, 0, 'h' },
  1568. { 0, 0, 0, 0 }
  1569. };
  1570. int option_index = 0;
  1571. int c = 0;
  1572. bool detach = false;
  1573. while ( ( c = getopt_long_only( argc, argv, "", long_options, &option_index ) ) != -1 )
  1574. {
  1575. switch ( c )
  1576. {
  1577. case 'd':
  1578. detach = true;
  1579. break;
  1580. case 's':
  1581. {
  1582. session_root = optarg;
  1583. /* get rid of trailing slash */
  1584. char *s = rindex(session_root,'/');
  1585. if ( s == &session_root[strlen(session_root) - 1] )
  1586. *s = '\0';
  1587. break;
  1588. }
  1589. case 'p':
  1590. DMESSAGE( "Using OSC port %s", optarg );
  1591. osc_port = optarg;
  1592. break;
  1593. case 'g':
  1594. DMESSAGE( "Going to connect to GUI at: %s", optarg );
  1595. gui_url = optarg;
  1596. break;
  1597. case 'h':
  1598. printf( "Usage: %s [--osc-port portnum] [--session-root path]\n\n", argv[0] );
  1599. exit(0);
  1600. break;
  1601. }
  1602. }
  1603. if ( !session_root )
  1604. asprintf( &session_root, "%s/%s", getenv( "HOME" ), "NSM Sessions" );
  1605. struct stat st;
  1606. if ( stat( session_root, &st ) )
  1607. {
  1608. if ( mkdir( session_root, 0771 ) )
  1609. {
  1610. FATAL( "Failed to create session directory: %s", strerror( errno ) );
  1611. }
  1612. }
  1613. MESSAGE( "Session root is: %s", session_root );
  1614. osc_server = new OSC::Endpoint();
  1615. if ( osc_server->init( LO_UDP, osc_port ) )
  1616. {
  1617. FATAL( "Failed to create OSC server." );
  1618. }
  1619. printf( "NSM_URL=%s\n", osc_server->url() );
  1620. if ( gui_url )
  1621. {
  1622. announce_gui( gui_url, false );
  1623. }
  1624. /* */
  1625. osc_server->add_method( "/nsm/server/announce", "sssiii", OSC_NAME( announce ), NULL, "client_name,capabilities,executable,api_version_major,api_version_minor,client_pid" );
  1626. /* response handlers */
  1627. osc_server->add_method( "/reply", "ss", OSC_NAME( reply ), NULL, "err_code,msg" );
  1628. osc_server->add_method( "/error", "sis", OSC_NAME( error ), NULL, "err_code,msg" );
  1629. osc_server->add_method( "/nsm/client/progress", "f", OSC_NAME( progress ), NULL, "progress" );
  1630. osc_server->add_method( "/nsm/client/is_dirty", "", OSC_NAME( is_dirty ), NULL, "dirtiness" );
  1631. osc_server->add_method( "/nsm/client/is_clean", "", OSC_NAME( is_clean ), NULL, "dirtiness" );
  1632. osc_server->add_method( "/nsm/client/message", "is", OSC_NAME( message ), NULL, "message" );
  1633. osc_server->add_method( "/nsm/client/gui_is_hidden", "", OSC_NAME( gui_is_hidden ), NULL, "message" );
  1634. osc_server->add_method( "/nsm/client/gui_is_shown", "", OSC_NAME( gui_is_shown ), NULL, "message" );
  1635. osc_server->add_method( "/nsm/client/label", "s", OSC_NAME( label ), NULL, "message" );
  1636. /* */
  1637. osc_server->add_method( "/nsm/gui/gui_announce", "", OSC_NAME( gui_announce ), NULL, "" );
  1638. osc_server->add_method( "/nsm/gui/client/stop", "s", OSC_NAME( stop ), NULL, "client_id" );
  1639. osc_server->add_method( "/nsm/gui/client/remove", "s", OSC_NAME( remove ), NULL, "client_id" );
  1640. osc_server->add_method( "/nsm/gui/client/resume", "s", OSC_NAME( resume ), NULL, "client_id" );
  1641. osc_server->add_method( "/nsm/gui/client/save", "s", OSC_NAME( client_save ), NULL, "client_id" );
  1642. osc_server->add_method( "/nsm/gui/client/show_optional_gui", "s", OSC_NAME( client_show_optional_gui ), NULL, "client_id" );
  1643. osc_server->add_method( "/nsm/gui/client/hide_optional_gui", "s", OSC_NAME( client_hide_optional_gui ), NULL, "client_id" );
  1644. osc_server->add_method( "/osc/ping", "", OSC_NAME( ping ), NULL, "" );
  1645. osc_server->add_method( "/nsm/server/broadcast", NULL, OSC_NAME( broadcast ), NULL, "" );
  1646. osc_server->add_method( "/nsm/server/duplicate", "s", OSC_NAME( duplicate ), NULL, "" );
  1647. osc_server->add_method( "/nsm/server/abort", "", OSC_NAME( abort ), NULL, "" );
  1648. osc_server->add_method( "/nsm/server/list", "", OSC_NAME( list ), NULL, "" );
  1649. osc_server->add_method( "/nsm/server/add", "s", OSC_NAME( add ), NULL, "commandline" );
  1650. osc_server->add_method( "/nsm/server/new", "s", OSC_NAME( new ), NULL, "name" );
  1651. osc_server->add_method( "/nsm/server/save", "", OSC_NAME( save ), NULL, "" );
  1652. osc_server->add_method( "/nsm/server/open", "s", OSC_NAME( open ), NULL, "name" );
  1653. osc_server->add_method( "/nsm/server/close", "", OSC_NAME( close ), NULL, "" );
  1654. osc_server->add_method( "/nsm/server/quit", "", OSC_NAME( quit ), NULL, "" );
  1655. if ( detach )
  1656. {
  1657. MESSAGE( "Detaching from console" );
  1658. if ( fork() )
  1659. {
  1660. exit( 0 );
  1661. }
  1662. else
  1663. {
  1664. fclose( stdin );
  1665. fclose( stdout );
  1666. fclose( stderr );
  1667. }
  1668. }
  1669. struct signalfd_siginfo fdsi;
  1670. /* listen for sigchld signals and process OSC messages forever */
  1671. for ( ;; )
  1672. {
  1673. ssize_t s = read(signal_fd, &fdsi, sizeof(struct signalfd_siginfo));
  1674. if (s == sizeof(struct signalfd_siginfo))
  1675. {
  1676. if (fdsi.ssi_signo == SIGCHLD)
  1677. handle_sigchld();
  1678. }
  1679. osc_server->wait( 1000 );
  1680. purge_dead_clients();
  1681. }
  1682. // osc_server->run();
  1683. return 0;
  1684. }