aRts audio server
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.

mcoputils.cc 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. /*
  2. Copyright (C) 2000 Stefan Westerfeld
  3. stefan@space.twc.de
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public
  6. License as published by the Free Software Foundation; either
  7. version 2 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Library General Public License for more details.
  12. You should have received a copy of the GNU Library General Public License
  13. along with this library; see the file COPYING.LIB. If not, write to
  14. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  15. Boston, MA 02110-1301, USA.
  16. */
  17. #include "mcoputils.h"
  18. #include "mcopconfig.h"
  19. #include "debug.h"
  20. #include <pwd.h>
  21. #include <sys/stat.h>
  22. #include <sys/types.h>
  23. #include <fcntl.h>
  24. #include <unistd.h>
  25. #include <netdb.h>
  26. #include <errno.h>
  27. #include <config.h>
  28. #include <ctype.h>
  29. #include <cstdlib>
  30. #include <cstring>
  31. #include <stdio.h>
  32. #include <map>
  33. #include <limits.h>
  34. using namespace std;
  35. using namespace Arts;
  36. // 0: Dir ok
  37. // 1: Dir not ok
  38. static int check_tmp_dir(const char *tmp_dir)
  39. {
  40. int result;
  41. struct stat stat_buf;
  42. result = lstat(tmp_dir, &stat_buf);
  43. if ((result == -1) && (errno == ENOENT))
  44. {
  45. result = mkdir(tmp_dir, 0700);
  46. if (result == -1)
  47. {
  48. arts_warning("Error: Cannot create directory \"%s\".\n", tmp_dir);
  49. return 1;
  50. }
  51. result = stat(tmp_dir, &stat_buf);
  52. }
  53. if ((result == -1) || (!S_ISDIR(stat_buf.st_mode)))
  54. {
  55. arts_warning("Error: \"%s\" is not a directory.\n", tmp_dir);
  56. return 1;
  57. }
  58. if (stat_buf.st_uid != getuid())
  59. {
  60. arts_warning("Error: \"%s\" is owned by uid %d instead of uid %d.\n", tmp_dir, stat_buf.st_uid, getuid());
  61. return 1;
  62. }
  63. return 0;
  64. }
  65. // 0: Link not ok
  66. // != 0: location of mcop directory
  67. static char *locate_mcop_dir()
  68. {
  69. struct passwd *pw_ent;
  70. string kde_tmp_dir;
  71. string user_tmp_dir;
  72. int uid = getuid();
  73. const char *home_dir = getenv("HOME");
  74. const char *kde_home = uid ? getenv("KDEHOME") : getenv("KDEROOTHOME");
  75. const char *kde_prefix = "/socket-";
  76. const char *tmp;
  77. char *tmp_buf;
  78. int result;
  79. struct stat stat_buf;
  80. tmp = getenv("KDETMP");
  81. if (!tmp || !tmp[0])
  82. tmp = getenv("TMPDIR");
  83. if (!tmp || !tmp[0])
  84. tmp = "/tmp";
  85. kde_tmp_dir = "";
  86. pw_ent = getpwuid(uid);
  87. if (!pw_ent)
  88. {
  89. arts_warning("Error: Can not find password entry for uid %d.\n", getuid());
  90. return 0;
  91. }
  92. user_tmp_dir = string(tmp) + "/ksocket-" + string(pw_ent->pw_name);
  93. if (!kde_home || !kde_home[0])
  94. {
  95. kde_home = "~/.kde/";
  96. }
  97. if (kde_home[0] == '~')
  98. {
  99. if (uid == 0)
  100. {
  101. home_dir = pw_ent->pw_dir ? pw_ent->pw_dir : "/root";
  102. }
  103. if (!home_dir || !home_dir[0])
  104. {
  105. arts_fatal("Aborting. $HOME not set!");
  106. }
  107. kde_home++;
  108. kde_tmp_dir = string(home_dir);
  109. }
  110. kde_tmp_dir += kde_home;
  111. /** Strip trailing '/' **/
  112. if ( kde_tmp_dir[kde_tmp_dir.length()-1] == '/')
  113. kde_tmp_dir.resize(kde_tmp_dir.length()-1);
  114. result = stat(kde_tmp_dir.c_str(), &stat_buf);
  115. if (result == -1)
  116. {
  117. return 0;
  118. }
  119. kde_tmp_dir += kde_prefix;
  120. {
  121. char buf[1024];
  122. if (gethostname(buf, sizeof(buf)-1) != 0)
  123. {
  124. arts_fatal("Aborting. Could not determine hostname or hostname too long.");
  125. }
  126. buf[sizeof(buf)-1] = '\0';
  127. kde_tmp_dir += buf;
  128. }
  129. result = lstat(kde_tmp_dir.c_str(), &stat_buf);
  130. if ((result == 0) && (S_ISDIR(stat_buf.st_mode)))
  131. {
  132. /* $KDEHOME/socket-$HOSTNAME is a normal directory. Do nothing. */
  133. tmp_buf = strdup(kde_tmp_dir.c_str());
  134. return tmp_buf;
  135. }
  136. if ((result == -1) && (errno == ENOENT))
  137. {
  138. // Link mising...
  139. return 0;
  140. }
  141. if ((result == -1) || (!S_ISLNK(stat_buf.st_mode)))
  142. {
  143. arts_warning("Error: \"%s\" is not a link or a directory.\n", kde_tmp_dir.c_str());
  144. return 0;
  145. }
  146. /* kde_tmp_dir is a link. Check whether it points to a valid directory. */
  147. ssize_t size = 2048;
  148. tmp_buf = NULL;
  149. do {
  150. size *= 2;
  151. tmp_buf = (char *) realloc(tmp_buf, size);
  152. if (!tmp_buf)
  153. return 0;
  154. result = readlink(kde_tmp_dir.c_str(), tmp_buf, size - 1);
  155. if (result == -1)
  156. {
  157. arts_warning("Error: \"%s\" could not be read.\n", kde_tmp_dir.c_str());
  158. free(tmp_buf);
  159. return 0;
  160. }
  161. } while(result == size - 1);
  162. tmp_buf[result] = '\0';
  163. // printf("Link points to \"%s\"\n", tmp_buf);
  164. if (strncmp(tmp_buf, user_tmp_dir.c_str(), user_tmp_dir.length()) != 0)
  165. {
  166. arts_warning("Error: \"%s\" points to \"%s\" instead of \"%s\".\n", kde_tmp_dir.c_str(), tmp_buf, user_tmp_dir.c_str());
  167. free(tmp_buf);
  168. return 0;
  169. }
  170. result = check_tmp_dir(tmp_buf);
  171. if (result == 0) return tmp_buf; /* Success */
  172. free(tmp_buf);
  173. return 0;
  174. }
  175. /* blatant and literal copy from lnusertemp to avoid kdelibs dependency */
  176. /* Copyright (c) 2000 Waldo Bastian <bastian@kde.org>, released under LGPL */
  177. static
  178. int create_link(const char *file, const char *tmp_dir)
  179. {
  180. int result;
  181. result = check_tmp_dir(tmp_dir);
  182. if (result)
  183. {
  184. return result;
  185. }
  186. result = symlink(tmp_dir, file);
  187. if (result == -1)
  188. {
  189. fprintf(stderr, "Error: Can not create link from \"%s\" to \"%s\"\n", file, tmp_dir);
  190. return 1;
  191. }
  192. printf("Created link from \"%s\" to \"%s\"\n", file, tmp_dir);
  193. return 0;
  194. }
  195. static
  196. int build_link(string tmp_prefix, const char *kde_prefix)
  197. {
  198. struct passwd *pw_ent;
  199. string kde_tmp_dir;
  200. string user_tmp_dir;
  201. char *tmp_buf;
  202. int uid = getuid();
  203. const char *home_dir = getenv("HOME");
  204. const char *kde_home = uid ? getenv("KDEHOME") : getenv("KDEROOTHOME");
  205. int result;
  206. struct stat stat_buf;
  207. kde_tmp_dir = "";
  208. pw_ent = getpwuid(uid);
  209. if (!pw_ent)
  210. {
  211. fprintf(stderr, "Error: Can not find password entry for uid %d.\n", getuid());
  212. return 1;
  213. }
  214. user_tmp_dir = tmp_prefix + string(pw_ent->pw_name);
  215. if (!kde_home || !kde_home[0])
  216. {
  217. kde_home = "~/.kde/";
  218. }
  219. if (kde_home[0] == '~')
  220. {
  221. if (uid == 0)
  222. {
  223. home_dir = pw_ent->pw_dir ? pw_ent->pw_dir : "/root";
  224. }
  225. if (!home_dir || !home_dir[0])
  226. {
  227. fprintf(stderr, "Aborting. $HOME not set!");
  228. exit(255);
  229. }
  230. kde_home++;
  231. kde_tmp_dir = string(home_dir);
  232. }
  233. kde_tmp_dir += kde_home;
  234. /** Strip trailing '/' **/
  235. if ( kde_tmp_dir[kde_tmp_dir.length()-1] == '/')
  236. kde_tmp_dir.resize(kde_tmp_dir.length()-1);
  237. result = stat(kde_tmp_dir.c_str(), &stat_buf);
  238. if ((result == -1) && (errno == ENOENT))
  239. {
  240. result = mkdir(kde_tmp_dir.c_str(), 0700);
  241. }
  242. if (result == -1)
  243. {
  244. return 1;
  245. }
  246. kde_tmp_dir += kde_prefix;
  247. {
  248. char buf[1024];
  249. if (gethostname(buf, sizeof(buf)-1) != 0)
  250. {
  251. arts_fatal("Aborting. Could not determine hostname or hostname too long.");
  252. }
  253. buf[sizeof(buf)-1] = '\0';
  254. kde_tmp_dir += buf;
  255. }
  256. result = lstat(kde_tmp_dir.c_str(), &stat_buf);
  257. if ((result == 0) && (S_ISDIR(stat_buf.st_mode)))
  258. {
  259. /* $KDEHOME/tmp is a normal directory. Do nothing. */
  260. printf("Directory \"%s\" already exists.\n", kde_tmp_dir.c_str());
  261. return 0;
  262. }
  263. if ((result == -1) && (errno == ENOENT))
  264. {
  265. printf("Creating link %s.\n", kde_tmp_dir.c_str());
  266. result = create_link(kde_tmp_dir.c_str(), user_tmp_dir.c_str());
  267. if (result == 0) return 0; /* Success */
  268. unlink(kde_tmp_dir.c_str());
  269. user_tmp_dir += "XXXXXX";
  270. tmp_buf = strdup(user_tmp_dir.c_str());
  271. mktemp(tmp_buf); /* We want a directory, not a file, so using mkstemp makes no sense and is wrong */
  272. result = create_link(kde_tmp_dir.c_str(), tmp_buf);
  273. free(tmp_buf);
  274. return result;
  275. }
  276. if ((result == -1) || (!S_ISLNK(stat_buf.st_mode)))
  277. {
  278. fprintf(stderr, "Error: \"%s\" is not a link or a directory.\n", kde_tmp_dir.c_str());
  279. return 1;
  280. }
  281. /* kde_tmp_dir is a link. Check whether it points to a valid directory. */
  282. ssize_t size = 2048;
  283. tmp_buf = NULL;
  284. do {
  285. size *= 2;
  286. tmp_buf = (char *) realloc(tmp_buf, size);
  287. if (!tmp_buf)
  288. return 0;
  289. result = readlink(kde_tmp_dir.c_str(), tmp_buf, size - 1);
  290. if (result == -1)
  291. {
  292. arts_warning("Error: \"%s\" could not be read.\n", kde_tmp_dir.c_str());
  293. free(tmp_buf);
  294. return 0;
  295. }
  296. } while(result == size - 1);
  297. tmp_buf[result] = '\0';
  298. printf("Link points to \"%s\"\n", tmp_buf);
  299. if (strncmp(tmp_buf, user_tmp_dir.c_str(), user_tmp_dir.length()) != 0)
  300. {
  301. fprintf(stderr, "Error: \"%s\" points to \"%s\" instead of \"%s\".\n", kde_tmp_dir.c_str(), tmp_buf, user_tmp_dir.c_str());
  302. free(tmp_buf);
  303. unlink(kde_tmp_dir.c_str());
  304. printf("Creating link %s.\n", kde_tmp_dir.c_str());
  305. result = create_link(kde_tmp_dir.c_str(), user_tmp_dir.c_str());
  306. if (result == 0) return 0; /* Success */
  307. unlink(kde_tmp_dir.c_str());
  308. user_tmp_dir += "XXXXXX";
  309. tmp_buf = strdup(user_tmp_dir.c_str());
  310. mktemp(tmp_buf); /* We want a directory, not a file, so using mkstemp makes no sense and is wrong */
  311. result = create_link(kde_tmp_dir.c_str(), tmp_buf);
  312. free(tmp_buf);
  313. return result;
  314. }
  315. result = check_tmp_dir(tmp_buf);
  316. free(tmp_buf);
  317. if (result == 0) return 0; /* Success */
  318. unlink(kde_tmp_dir.c_str());
  319. user_tmp_dir += "XXXXXX";
  320. tmp_buf = strdup(user_tmp_dir.c_str());
  321. mktemp(tmp_buf); /* We want a directory, not a file, so using mkstemp makes no sense and is wrong */
  322. result = create_link(kde_tmp_dir.c_str(), tmp_buf);
  323. free(tmp_buf);
  324. return result;
  325. }
  326. string MCOPUtils::createFilePath(string name)
  327. {
  328. static char *mcop_dir = 0;
  329. if (!mcop_dir)
  330. {
  331. mcop_dir = locate_mcop_dir();
  332. }
  333. if (!mcop_dir)
  334. {
  335. const char *tmp = 0;
  336. tmp = getenv("KDETMP");
  337. if (!tmp || !tmp[0])
  338. tmp = getenv("TMPDIR");
  339. if (!tmp || !tmp[0])
  340. tmp = "/tmp";
  341. build_link(string(tmp) + "/ksocket-", "/socket-");
  342. mcop_dir = locate_mcop_dir();
  343. }
  344. if (!mcop_dir)
  345. {
  346. arts_fatal("can't create mcop directory");
  347. }
  348. string tmpdir = mcop_dir;
  349. return tmpdir+"/"+name;
  350. }
  351. /*
  352. * try to figure out full hostname - this is important as every client which
  353. * tries to connect objects located here will need to be able to resolve that
  354. * correctly
  355. */
  356. string MCOPUtils::getFullHostname()
  357. {
  358. char buffer[1024];
  359. string result;
  360. struct hostent *hp;
  361. if(gethostname(buffer,1024) == 0) {
  362. buffer[1023] = '\0';
  363. result = buffer;
  364. } else
  365. return "localhost";
  366. /*
  367. * if gethostname() isn't a FQDN (guess that by checking whether it
  368. * contains a dot), try to look it up to ensure it is
  369. */
  370. if(result.find('.') == string::npos && (hp = gethostbyname(buffer)) != 0)
  371. result = hp->h_name;
  372. return result;
  373. }
  374. string MCOPUtils::readConfigEntry(const string& key, const string& defaultValue)
  375. {
  376. const char *home = getenv("HOME");
  377. if(home)
  378. {
  379. string rcname = home + string("/.mcoprc");
  380. MCOPConfig config(rcname);
  381. return config.readEntry(key,defaultValue);
  382. }
  383. return defaultValue;
  384. }
  385. /** IID generation **/
  386. unsigned long MCOPUtils::makeIID(const string& interfaceName)
  387. {
  388. static map<string, unsigned long> *iidmapobj = 0;
  389. static unsigned long nextiid = 1;
  390. if(!iidmapobj) iidmapobj = new map<string,unsigned long>;
  391. map<string,unsigned long>& iidmap = *iidmapobj;
  392. if(iidmap.find(interfaceName) == iidmap.end()) {
  393. iidmap[interfaceName] = nextiid++;
  394. return nextiid-1; // no need to lookup in the map thrice
  395. }
  396. return iidmap[interfaceName];
  397. }
  398. static vector<string> *readPath(const string& name, const string& defaultValue)
  399. {
  400. vector<string> *result = 0;
  401. const char *home = getenv("HOME");
  402. if(home)
  403. {
  404. string rcname = home + string("/.mcoprc");
  405. MCOPConfig config(rcname);
  406. result = config.readListEntry(name);
  407. }
  408. if(!result)
  409. result = new vector<string>;
  410. if(result->empty())
  411. result->push_back(defaultValue);
  412. return result;
  413. }
  414. const vector<string> *MCOPUtils::extensionPath()
  415. {
  416. static vector<string> *result = 0;
  417. if(!result) result = readPath("ExtensionPath", EXTENSION_DIR);
  418. return result;
  419. }
  420. const vector<string> *MCOPUtils::traderPath()
  421. {
  422. static vector<string> *result = 0;
  423. if(!result)
  424. {
  425. result = readPath("TraderPath", TRADER_DIR);
  426. const char *home = getenv("HOME");
  427. if(home) result->push_back(home + string("/.mcop/trader-cache"));
  428. }
  429. return result;
  430. }
  431. string MCOPUtils::mcopDirectory()
  432. {
  433. static bool initialized = false;
  434. static string mcopDirectory;
  435. if(initialized)
  436. return mcopDirectory;
  437. initialized = true;
  438. const char *home = getenv("HOME");
  439. arts_return_val_if_fail(home != 0, "");
  440. mcopDirectory = home + string("/.mcop");
  441. mkdir(home,0755);
  442. if(mkdir(mcopDirectory.c_str(),0755) != 0)
  443. {
  444. string why = strerror(errno);
  445. struct stat st;
  446. stat(mcopDirectory.c_str(),&st);
  447. if(!S_ISDIR(st.st_mode))
  448. {
  449. arts_warning("can't create directory %s (%s)",
  450. mcopDirectory.c_str(), why.c_str());
  451. mcopDirectory = "";
  452. }
  453. }
  454. return mcopDirectory;
  455. }
  456. bool MCOPUtils::tokenize(const string& line, string& key,vector<string>& values)
  457. {
  458. string value;
  459. enum { sKey, sValue, sValueQuoted, sValueQuotedEscaped, sBad } state;
  460. state = sKey;
  461. for(string::const_iterator i = line.begin(); i != line.end(); i++)
  462. {
  463. char c = *i;
  464. unsigned char uc = static_cast<unsigned char>(c);
  465. arts_assert(c != '\n');
  466. if(state == sKey)
  467. {
  468. if(c == ' ' || c == '\t')
  469. ; // ignore
  470. else if(isalnum(c))
  471. key += c;
  472. else if(c == '=')
  473. state = sValue;
  474. else
  475. state = sBad;
  476. }
  477. else if(state == sValue)
  478. {
  479. if(c == ' ' || c == '\t')
  480. ; // ignore
  481. else if(c == '"')
  482. state = sValueQuoted;
  483. else if(c == ',')
  484. {
  485. values.push_back(value);
  486. value = "";
  487. }
  488. else if(uc > 32 && uc < 128)
  489. value += c;
  490. else
  491. state = sBad;
  492. }
  493. else if(state == sValueQuoted)
  494. {
  495. if(c == '"')
  496. state = sValue;
  497. else if(c == '\\')
  498. state = sValueQuotedEscaped;
  499. else
  500. value += c;
  501. }
  502. else if(state == sValueQuotedEscaped)
  503. {
  504. value += c;
  505. state = sValueQuoted;
  506. }
  507. }
  508. if(state == sValue)
  509. values.push_back(value);
  510. return(state != sBad);
  511. }