jack2 codebase
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.

411 lines
13KB

  1. /*
  2. Copyright (C) 2006-2008 Grame
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU Lesser General Public License as published by
  5. the Free Software Foundation; either version 2.1 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. */
  15. #if defined(HAVE_CONFIG_H)
  16. #include "config.h"
  17. #endif
  18. #include "JackConstants.h"
  19. #include "JackDriverLoader.h"
  20. #include "JackTools.h"
  21. #include <stdlib.h>
  22. #include <stdio.h>
  23. #include <assert.h>
  24. #ifdef WIN32
  25. #include <process.h>
  26. #endif
  27. using namespace std;
  28. namespace Jack {
  29. #define DEFAULT_TMP_DIR "/tmp"
  30. char* jack_tmpdir = (char*)DEFAULT_TMP_DIR;
  31. int JackTools::GetPID()
  32. {
  33. #ifdef WIN32
  34. return _getpid();
  35. #else
  36. return getpid();
  37. #endif
  38. }
  39. int JackTools::GetUID()
  40. {
  41. #ifdef WIN32
  42. return _getpid();
  43. //#error "No getuid function available"
  44. #else
  45. return getuid();
  46. #endif
  47. }
  48. const char* JackTools::DefaultServerName()
  49. {
  50. const char* server_name;
  51. if ((server_name = getenv("JACK_DEFAULT_SERVER")) == NULL)
  52. server_name = JACK_DEFAULT_SERVER_NAME;
  53. return server_name;
  54. }
  55. /* returns the name of the per-user subdirectory of jack_tmpdir */
  56. #ifdef WIN32
  57. char* JackTools::UserDir()
  58. {
  59. return "";
  60. }
  61. char* JackTools::ServerDir(const char* server_name, char* server_dir)
  62. {
  63. return "";
  64. }
  65. void JackTools::CleanupFiles(const char* server_name) {}
  66. int JackTools::GetTmpdir()
  67. {
  68. return 0;
  69. }
  70. #else
  71. char* JackTools::UserDir()
  72. {
  73. static char user_dir[JACK_PATH_MAX + 1] = "";
  74. /* format the path name on the first call */
  75. if (user_dir[0] == '\0') {
  76. if (getenv ("JACK_PROMISCUOUS_SERVER")) {
  77. snprintf(user_dir, sizeof(user_dir), "%s/jack", jack_tmpdir);
  78. } else {
  79. snprintf(user_dir, sizeof(user_dir), "%s/jack-%d", jack_tmpdir, GetUID());
  80. }
  81. }
  82. return user_dir;
  83. }
  84. /* returns the name of the per-server subdirectory of jack_user_dir() */
  85. char* JackTools::ServerDir(const char* server_name, char* server_dir)
  86. {
  87. /* format the path name into the suppled server_dir char array,
  88. * assuming that server_dir is at least as large as JACK_PATH_MAX + 1 */
  89. snprintf(server_dir, JACK_PATH_MAX + 1, "%s/%s", UserDir(), server_name);
  90. return server_dir;
  91. }
  92. void JackTools::CleanupFiles(const char* server_name)
  93. {
  94. DIR* dir;
  95. struct dirent *dirent;
  96. char dir_name[JACK_PATH_MAX + 1] = "";
  97. ServerDir(server_name, dir_name);
  98. /* On termination, we remove all files that jackd creates so
  99. * subsequent attempts to start jackd will not believe that an
  100. * instance is already running. If the server crashes or is
  101. * terminated with SIGKILL, this is not possible. So, cleanup
  102. * is also attempted when jackd starts.
  103. *
  104. * There are several tricky issues. First, the previous JACK
  105. * server may have run for a different user ID, so its files
  106. * may be inaccessible. This is handled by using a separate
  107. * JACK_TMP_DIR subdirectory for each user. Second, there may
  108. * be other servers running with different names. Each gets
  109. * its own subdirectory within the per-user directory. The
  110. * current process has already registered as `server_name', so
  111. * we know there is no other server actively using that name.
  112. */
  113. /* nothing to do if the server directory does not exist */
  114. if ((dir = opendir(dir_name)) == NULL) {
  115. return;
  116. }
  117. /* unlink all the files in this directory, they are mine */
  118. while ((dirent = readdir(dir)) != NULL) {
  119. char fullpath[JACK_PATH_MAX + 1];
  120. if ((strcmp(dirent->d_name, ".") == 0) || (strcmp (dirent->d_name, "..") == 0)) {
  121. continue;
  122. }
  123. snprintf(fullpath, sizeof(fullpath), "%s/%s", dir_name, dirent->d_name);
  124. if (unlink(fullpath)) {
  125. jack_error("cannot unlink `%s' (%s)", fullpath, strerror(errno));
  126. }
  127. }
  128. closedir(dir);
  129. /* now, delete the per-server subdirectory, itself */
  130. if (rmdir(dir_name)) {
  131. jack_error("cannot remove `%s' (%s)", dir_name, strerror(errno));
  132. }
  133. /* finally, delete the per-user subdirectory, if empty */
  134. if (rmdir(UserDir())) {
  135. if (errno != ENOTEMPTY) {
  136. jack_error("cannot remove `%s' (%s)", UserDir(), strerror(errno));
  137. }
  138. }
  139. }
  140. int JackTools::GetTmpdir() {
  141. FILE* in;
  142. size_t len;
  143. char buf[JACK_PATH_MAX + 2]; /* allow tmpdir to live anywhere, plus newline, plus null */
  144. if ((in = popen("jackd -l", "r")) == NULL) {
  145. return -1;
  146. }
  147. if (fgets(buf, sizeof(buf), in) == NULL) {
  148. fclose(in);
  149. return -1;
  150. }
  151. len = strlen(buf);
  152. if (buf[len - 1] != '\n') {
  153. /* didn't get a whole line */
  154. fclose(in);
  155. return -1;
  156. }
  157. jack_tmpdir = (char *)malloc(len);
  158. memcpy(jack_tmpdir, buf, len - 1);
  159. jack_tmpdir[len - 1] = '\0';
  160. fclose(in);
  161. return 0;
  162. }
  163. #endif
  164. void JackTools::RewriteName(const char* name, char* new_name) {
  165. size_t i;
  166. for (i = 0; i < strlen(name); i++) {
  167. if ((name[i] == '/') || (name[i] == '\\'))
  168. new_name[i] = '_';
  169. else
  170. new_name[i] = name[i];
  171. }
  172. new_name[i] = '\0';
  173. }
  174. // class JackArgParser ***************************************************
  175. JackArgParser::JackArgParser ( const char* arg )
  176. {
  177. jack_log ( "JackArgParser::JackArgParser, arg_string : '%s'", arg );
  178. fArgc = 0;
  179. //if empty string
  180. if ( strlen(arg) == 0 )
  181. return;
  182. fArgString = string(arg);
  183. //else parse the arg string
  184. const size_t arg_len = fArgString.length();
  185. unsigned int i = 0;
  186. size_t pos = 0;
  187. size_t start = 0;
  188. size_t copy_start = 0;
  189. size_t copy_length = 0;
  190. //we need a 'space terminated' string
  191. fArgString += " ";
  192. //first fill a vector with args
  193. do {
  194. //find the first non-space character from the actual position
  195. start = fArgString.find_first_not_of ( ' ', start );
  196. //get the next quote or space position
  197. pos = fArgString.find_first_of ( " \"" , start );
  198. //no more quotes or spaces, consider the end of the string
  199. if ( pos == string::npos )
  200. pos = arg_len;
  201. //if double quote
  202. if ( fArgString[pos] == '\"' ) {
  203. //first character : copy the substring
  204. if ( pos == start ) {
  205. copy_start = start + 1;
  206. pos = fArgString.find ( '\"', ++pos );
  207. copy_length = pos - copy_start;
  208. start = pos + 1;
  209. }
  210. //else there is someting before the quote, first copy that
  211. else {
  212. copy_start = start;
  213. copy_length = pos - copy_start;
  214. start = pos;
  215. }
  216. }
  217. //if space
  218. if ( fArgString[pos] == ' ' ) {
  219. //short option descriptor
  220. if ( ( fArgString[start] == '-' ) && ( fArgString[start + 1] != '-' ) ) {
  221. copy_start = start;
  222. copy_length = 2;
  223. start += copy_length;
  224. }
  225. //else copy all the space delimitated string
  226. else {
  227. copy_start = start;
  228. copy_length = pos - copy_start;
  229. start = pos + 1;
  230. }
  231. }
  232. //then push the substring to the args vector
  233. fArgv.push_back ( fArgString.substr ( copy_start, copy_length ) );
  234. jack_log ( "JackArgParser::JackArgParser, add : '%s'", (*fArgv.rbegin()).c_str() );
  235. } while ( start < arg_len );
  236. //finally count the options
  237. for ( i = 0; i < fArgv.size(); i++ )
  238. if ( fArgv[i].at(0) == '-' )
  239. fArgc++;
  240. }
  241. JackArgParser::~JackArgParser()
  242. {}
  243. string JackArgParser::GetArgString()
  244. {
  245. return fArgString;
  246. }
  247. int JackArgParser::GetNumArgv()
  248. {
  249. return fArgv.size();
  250. }
  251. int JackArgParser::GetArgc()
  252. {
  253. return fArgc;
  254. }
  255. int JackArgParser::GetArgv ( vector<string>& argv )
  256. {
  257. argv = fArgv;
  258. return 0;
  259. }
  260. int JackArgParser::GetArgv ( char** argv )
  261. {
  262. //argv must be NULL
  263. if ( argv )
  264. return -1;
  265. //else allocate and fill it
  266. argv = (char**)calloc (fArgv.size(), sizeof(char*));
  267. for ( unsigned int i = 0; i < fArgv.size(); i++ )
  268. {
  269. argv[i] = (char*)calloc(fArgv[i].length(), sizeof(char));
  270. fill_n ( argv[i], fArgv[i].length() + 1, 0 );
  271. fArgv[i].copy ( argv[i], fArgv[i].length() );
  272. }
  273. return 0;
  274. }
  275. void JackArgParser::DeleteArgv ( const char** argv )
  276. {
  277. unsigned int i;
  278. for ( i = 0; i < fArgv.size(); i++ )
  279. free((void*)argv[i]);
  280. free((void*)argv);
  281. }
  282. void JackArgParser::ParseParams ( jack_driver_desc_t* desc, JSList** param_list )
  283. {
  284. string options_list;
  285. unsigned long i = 0;
  286. unsigned int param = 0;
  287. size_t param_id = 0;
  288. JSList* params = NULL;
  289. jack_driver_param_t* intclient_param;
  290. for ( i = 0; i < desc->nparams; i++ )
  291. options_list += desc->params[i].character;
  292. for ( param = 0; param < fArgv.size(); param++ )
  293. {
  294. if ( fArgv[param][0] == '-' )
  295. {
  296. //valid option
  297. if ( ( param_id = options_list.find_first_of ( fArgv[param].at(1) ) ) != string::npos )
  298. {
  299. intclient_param = static_cast<jack_driver_param_t*> ( calloc ( 1, sizeof ( jack_driver_param_t) ) );
  300. intclient_param->character = desc->params[param_id].character;
  301. switch ( desc->params[param_id].type )
  302. {
  303. case JackDriverParamInt:
  304. if (param + 1 < fArgv.size()) // something to parse
  305. intclient_param->value.i = atoi ( fArgv[param + 1].c_str() );
  306. break;
  307. case JackDriverParamUInt:
  308. if (param + 1 < fArgv.size()) // something to parse
  309. intclient_param->value.ui = strtoul ( fArgv[param + 1].c_str(), NULL, 10 );
  310. break;
  311. case JackDriverParamChar:
  312. if (param + 1 < fArgv.size()) // something to parse
  313. intclient_param->value.c = fArgv[param + 1][0];
  314. break;
  315. case JackDriverParamString:
  316. if (param + 1 < fArgv.size()) // something to parse
  317. fArgv[param + 1].copy ( intclient_param->value.str, min(static_cast<int>(fArgv[param + 1].length()), JACK_DRIVER_PARAM_STRING_MAX) );
  318. break;
  319. case JackDriverParamBool:
  320. intclient_param->value.i = true;
  321. break;
  322. }
  323. //add to the list
  324. params = jack_slist_append ( params, intclient_param );
  325. }
  326. //invalid option
  327. else
  328. jack_error ( "Invalid option '%c'", fArgv[param][1] );
  329. }
  330. }
  331. assert(param_list);
  332. *param_list = params;
  333. }
  334. void JackArgParser::FreeParams ( JSList* param_list )
  335. {
  336. JSList *node_ptr = param_list;
  337. JSList *next_node_ptr;
  338. while (node_ptr) {
  339. next_node_ptr = node_ptr->next;
  340. free(node_ptr->data);
  341. free(node_ptr);
  342. node_ptr = next_node_ptr;
  343. }
  344. }
  345. }