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.

2365 lines
55KB

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