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.

2131 lines
50KB

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