protect_db.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. /** BEGIN COPYRIGHT BLOCK
  2. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  3. * Copyright (C) 2005 Red Hat, Inc.
  4. * All rights reserved.
  5. *
  6. * License: GPL (version 3 or any later version).
  7. * See LICENSE for details.
  8. * END COPYRIGHT BLOCK **/
  9. #ifdef HAVE_CONFIG_H
  10. # include <config.h>
  11. #endif
  12. /* protect_db.c
  13. * Used to police access to the db. Prevents different instances of
  14. * slapd from clobbering each other
  15. */
  16. #define LOCK_FILE "lock"
  17. #define IMPORT_DIR "imports"
  18. #define EXPORT_DIR "exports"
  19. #define SERVER_DIR "server"
  20. #define NUM_TRIES 20
  21. #define WAIT_TIME 250
  22. #include <sys/types.h>
  23. #include <sys/time.h>
  24. #include <sys/stat.h>
  25. #include <fcntl.h> /* open */
  26. #include <unistd.h>
  27. #include <errno.h>
  28. #include <signal.h>
  29. #include <sys/param.h> /* MAXPATHLEN */
  30. #include <dirent.h>
  31. #include <pwd.h>
  32. #include "protect_db.h"
  33. #include "slap.h"
  34. static int
  35. grab_lockfile()
  36. {
  37. pid_t pid, owning_pid;
  38. char lockfile[MAXPATHLEN];
  39. int fd, x;
  40. int removed_lockfile = 0;
  41. struct timeval t;
  42. slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
  43. /* Don't use anything that makes a NSPR call here (like LDAPDebug)! This function
  44. gets called by an atexit function, and NSPR is long gone by then. */
  45. /* Get the name of the lockfile */
  46. snprintf(lockfile, sizeof(lockfile), "%s/%s", slapdFrontendConfig->lockdir, LOCK_FILE);
  47. lockfile[sizeof(lockfile)-1] = (char)0;
  48. /* Get our pid */
  49. pid = getpid();
  50. /* Try to grab it */
  51. if ((fd = open(lockfile, O_RDWR | O_CREAT | O_EXCL, 0644)) != -1) {
  52. /* We got the lock, write our pid to the file */
  53. write(fd, (void *) &pid, sizeof(pid_t));
  54. close(fd);
  55. return 0;
  56. }
  57. /* We weren't able to get the lock. Find out why. */
  58. if (errno != EEXIST) {
  59. /* Hmm, something happened that we weren't prepared to handle */
  60. fprintf(stderr, ERROR_ACCESSING_LOCKFILE, lockfile);
  61. return -1;
  62. }
  63. while(1) {
  64. /* Try to grab the lockfile NUM_TRIES times waiting WAIT_TIME milliseconds after each try */
  65. t.tv_sec = 0;
  66. t.tv_usec = WAIT_TIME * 1000;
  67. for(x = 0; x < NUM_TRIES; x++) {
  68. if ((fd = open(lockfile, O_RDWR | O_CREAT | O_EXCL, 0644)) != -1) {
  69. /* Got the lock */
  70. write(fd, (void *) &pid, sizeof(pid_t));
  71. close(fd);
  72. return 0;
  73. }
  74. select(0, NULL, NULL, NULL, &t);
  75. }
  76. /* We still haven't got the lockfile. Find out who owns it and see if they are still up */
  77. if ((fd = open(lockfile, O_RDONLY)) != -1) {
  78. size_t nb_bytes=0;
  79. nb_bytes = read(fd, (void *) &owning_pid, sizeof(pid_t));
  80. if ( (nb_bytes != (size_t)(sizeof(pid_t)) ) || (owning_pid == 0) || (kill(owning_pid, 0) != 0 && errno == ESRCH) ) {
  81. /* The process that owns the lock is dead. Try to remove the old lockfile. */
  82. if (unlink(lockfile) != 0) {
  83. /* Unable to remove the stale lockfile. */
  84. fprintf(stderr, LOCKFILE_DEAD_OWNER, lockfile, owning_pid);
  85. return -1;
  86. }
  87. if (removed_lockfile) {
  88. /* We already removed the lockfile once. Best thing to do is give up. */
  89. /* I'm not sure what should be given as an error here */
  90. fprintf(stderr, UNABLE_TO_GET_LOCKFILE, lockfile);
  91. return -1;
  92. } else {
  93. removed_lockfile = 1;
  94. }
  95. } else {
  96. /* It looks like the owner of the lockfile is still up and running. */
  97. fprintf(stderr, LOCKFILE_ALREADY_OWNED, owning_pid);
  98. return -1;
  99. }
  100. }
  101. }
  102. }
  103. static void
  104. release_lockfile()
  105. {
  106. char lockfile[MAXPATHLEN];
  107. slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
  108. /* This function assumes that the caller owns the lock, it doesn't check to make sure! */
  109. snprintf(lockfile, sizeof(lockfile), "%s/%s", slapdFrontendConfig->lockdir, LOCK_FILE);
  110. lockfile[sizeof(lockfile)-1] = (char)0;
  111. unlink(lockfile);
  112. }
  113. /* Takes the pid of a process. Returns 1 if the process seems
  114. * to be up, otherwise it returns 0.
  115. */
  116. static int
  117. is_process_up(pid_t pid)
  118. {
  119. if (kill(pid, 0) == -1 && errno == ESRCH) {
  120. return 0;
  121. } else {
  122. return 1;
  123. }
  124. }
  125. /* Make sure the directory 'dir' exists and is owned bye the user
  126. * the server runs as. Returns 0 if everything is ok.
  127. */
  128. static int
  129. make_sure_dir_exists(char *dir)
  130. {
  131. struct passwd* pw;
  132. struct stat stat_buffer;
  133. slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
  134. /* Make sure it exists */
  135. if (PR_MkDir(dir, 0755) != PR_SUCCESS) {
  136. PRErrorCode prerr = PR_GetError();
  137. if (prerr != PR_FILE_EXISTS_ERROR) {
  138. LDAPDebug(LDAP_DEBUG_ANY, FILE_CREATE_ERROR, dir, prerr, slapd_pr_strerror(prerr));
  139. return 1;
  140. }
  141. }
  142. /* Make sure it's owned by the correct user */
  143. if (slapdFrontendConfig->localuser != NULL &&
  144. slapdFrontendConfig->localuserinfo != NULL) {
  145. pw = slapdFrontendConfig->localuserinfo;
  146. if (chown(dir, pw->pw_uid, -1) == -1) {
  147. if ((stat(dir, &stat_buffer) == 0) && (stat_buffer.st_uid != pw->pw_uid)) {
  148. LDAPDebug(LDAP_DEBUG_ANY, CHOWN_WARNING, dir, 0, 0);
  149. return 1;
  150. } else {
  151. LDAPDebug(LDAP_DEBUG_ANY, STAT_ERROR, dir, errno, 0);
  152. return 1;
  153. }
  154. }
  155. }
  156. return 0;
  157. }
  158. /* Creates a file in the directory 'dir_name' whose name is the
  159. * pid for this process.
  160. */
  161. static void
  162. add_this_process_to(char *dir_name)
  163. {
  164. char file_name[MAXPATHLEN];
  165. struct passwd* pw;
  166. struct stat stat_buffer;
  167. PRFileDesc* prfd;
  168. slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
  169. snprintf(file_name, sizeof(file_name), "%s/%d", dir_name, getpid());
  170. file_name[sizeof(file_name)-1] = (char)0;
  171. if ((prfd = PR_Open(file_name, PR_RDWR | PR_CREATE_FILE, 0644)) == NULL) {
  172. LDAPDebug(LDAP_DEBUG_ANY, FILE_CREATE_WARNING, file_name, 0, 0);
  173. return;
  174. }
  175. /* Make sure the owner is of the file is the user the server
  176. * runs as. */
  177. if (slapdFrontendConfig->localuser != NULL &&
  178. slapdFrontendConfig->localuserinfo != NULL) {
  179. pw = slapdFrontendConfig->localuserinfo;
  180. if (chown(file_name, pw->pw_uid, -1) == -1) {
  181. if ((stat(file_name, &stat_buffer) == 0) && (stat_buffer.st_uid != pw->pw_uid)) {
  182. LDAPDebug(LDAP_DEBUG_ANY, CHOWN_WARNING, file_name, 0, 0);
  183. }
  184. }
  185. }
  186. PR_Close(prfd);
  187. }
  188. /* The directory 'dir_name' is expected to contain files whose
  189. * names are the pid of running slapd processes. This
  190. * function will check each entry in the directory and remove
  191. * any files that represent processes that don't exist.
  192. * Returns 0 if there are no processes represented, or
  193. * the pid of one of the processes.
  194. */
  195. static long
  196. sample_and_update(char *dir_name)
  197. {
  198. PRDir *dir;
  199. PRDirEntry *entry;
  200. pid_t pid;
  201. long result = 0;
  202. char *endp;
  203. char file_name[MAXPATHLEN];
  204. if ((dir = PR_OpenDir(dir_name)) == NULL) {
  205. return 0;
  206. }
  207. while((entry = PR_ReadDir(dir, PR_SKIP_BOTH)) != NULL) {
  208. pid = (pid_t) strtol(entry->name, &endp, 0);
  209. if (*endp != '\0') {
  210. /* not quite sure what this file was, but we
  211. * didn't put it there */
  212. continue;
  213. }
  214. if (is_process_up(pid)) {
  215. result = (long) pid;
  216. } else {
  217. PR_snprintf(file_name, MAXPATHLEN, "%s/%s", dir_name, entry->name);
  218. PR_Delete(file_name);
  219. }
  220. }
  221. PR_CloseDir(dir);
  222. return result;
  223. }
  224. /* Removes any stale pid entries and the pid entry for this
  225. * process from the directory 'dir_name'.
  226. */
  227. static void
  228. remove_and_update(char *dir_name)
  229. {
  230. /* since this is called from an atexit function, we can't use
  231. * NSPR. */
  232. DIR *dir;
  233. struct dirent *entry;
  234. pid_t pid;
  235. pid_t our_pid;
  236. char *endp;
  237. char file_name[MAXPATHLEN];
  238. /* get our pid */
  239. our_pid = getpid();
  240. if ((dir = opendir(dir_name)) == NULL) {
  241. return;
  242. }
  243. while((entry = readdir(dir)) != NULL) {
  244. /* skip dot and dot-dot */
  245. if (strcmp(entry->d_name, ".") == 0 ||
  246. strcmp(entry->d_name, "..") == 0)
  247. continue;
  248. pid = (pid_t) strtol(entry->d_name, &endp, 0);
  249. if (*endp != '\0') {
  250. /* not quite sure what this file was, but we
  251. * didn't put it there */
  252. continue;
  253. }
  254. if (!is_process_up(pid) || pid == our_pid) {
  255. PR_snprintf(file_name, sizeof(file_name), "%s/%s", dir_name, entry->d_name);
  256. unlink(file_name);
  257. }
  258. }
  259. closedir(dir);
  260. }
  261. /* Walks through all the pid directories and clears any stale
  262. * pids. It also removes the files for this process.
  263. */
  264. void
  265. remove_slapd_process()
  266. {
  267. char import_dir[MAXPATHLEN];
  268. char export_dir[MAXPATHLEN];
  269. char server_dir[MAXPATHLEN];
  270. slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
  271. /* Create the name of the directories that hold the pids of the currently running
  272. * ns-slapd processes */
  273. snprintf(import_dir, sizeof(import_dir), "%s/%s", slapdFrontendConfig->lockdir, IMPORT_DIR);
  274. import_dir[sizeof(import_dir)-1] = (char)0;
  275. snprintf(export_dir, sizeof(export_dir), "%s/%s", slapdFrontendConfig->lockdir, EXPORT_DIR);
  276. export_dir[sizeof(export_dir)-1] = (char)0;
  277. snprintf(server_dir, sizeof(server_dir), "%s/%s", slapdFrontendConfig->lockdir, SERVER_DIR);
  278. server_dir[sizeof(server_dir)-1] = (char)0;
  279. /* Grab the lockfile */
  280. if (grab_lockfile() != 0) {
  281. /* Unable to grab the lockfile */
  282. return;
  283. }
  284. remove_and_update(import_dir);
  285. remove_and_update(export_dir);
  286. remove_and_update(server_dir);
  287. release_lockfile();
  288. }
  289. int
  290. add_new_slapd_process(int exec_mode, int r_flag, int skip_flag)
  291. {
  292. char import_dir[MAXPATHLEN];
  293. char export_dir[MAXPATHLEN];
  294. char server_dir[MAXPATHLEN];
  295. int running, importing, exporting;
  296. int result = 0;
  297. slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
  298. if (skip_flag) {
  299. return 0;
  300. }
  301. /* Create the name of the directories that hold the pids of the currently running
  302. * ns-slapd processes */
  303. snprintf(import_dir, sizeof(import_dir), "%s/%s", slapdFrontendConfig->lockdir, IMPORT_DIR);
  304. import_dir[sizeof(import_dir)-1] = (char)0;
  305. snprintf(export_dir, sizeof(export_dir), "%s/%s", slapdFrontendConfig->lockdir, EXPORT_DIR);
  306. export_dir[sizeof(export_dir)-1] = (char)0;
  307. snprintf(server_dir, sizeof(server_dir), "%s/%s", slapdFrontendConfig->lockdir, SERVER_DIR);
  308. server_dir[sizeof(server_dir)-1] = (char)0;
  309. /* Grab the lockfile */
  310. if (grab_lockfile() != 0) {
  311. /* Unable to grab the lockfile */
  312. return -1;
  313. }
  314. /* Make sure the directories exist */
  315. if (make_sure_dir_exists(slapdFrontendConfig->lockdir) != 0 ||
  316. make_sure_dir_exists(import_dir) != 0 ||
  317. make_sure_dir_exists(export_dir) != 0 ||
  318. make_sure_dir_exists(server_dir) != 0) {
  319. release_lockfile();
  320. return -1;
  321. }
  322. /* Go through the directories and find out what's going on.
  323. * Clear any stale pids encountered. */
  324. importing = sample_and_update(import_dir);
  325. exporting = sample_and_update(export_dir);
  326. running = sample_and_update(server_dir);
  327. switch (exec_mode) {
  328. case SLAPD_EXEMODE_SLAPD:
  329. if (running) {
  330. result = -1;
  331. LDAPDebug(LDAP_DEBUG_ANY, NO_SERVER_DUE_TO_SERVER, running, 0, 0);
  332. } else if (importing) {
  333. result = -1;
  334. LDAPDebug(LDAP_DEBUG_ANY, NO_SERVER_DUE_TO_IMPORT, importing, 0, 0);
  335. } else {
  336. add_this_process_to(server_dir);
  337. result = 0;
  338. }
  339. break;
  340. case SLAPD_EXEMODE_DB2LDIF:
  341. if (r_flag) {
  342. /* When the -r flag is used in db2ldif we need to make sure
  343. * we get a consistent snapshot of the server. As a result
  344. * it needs to run by itself, so no other slapd process can
  345. * change the database while it is running. */
  346. if (running || importing) {
  347. LDAPDebug(LDAP_DEBUG_ANY, NO_DB2LDIFR_DUE_TO_USE, 0, 0, 0);
  348. result = -1;
  349. } else {
  350. /* Even though this is really going to export code, we will
  351. * but it in the importing dir so no other process can change
  352. * things while we are doing ldif2db with the -r flag. */
  353. add_this_process_to(import_dir);
  354. result = 0;
  355. }
  356. } else {
  357. if (importing) {
  358. LDAPDebug(LDAP_DEBUG_ANY, NO_DB2LDIF_DUE_TO_IMPORT, importing, 0, 0);
  359. result = -1;
  360. } else {
  361. add_this_process_to(export_dir);
  362. result = 0;
  363. }
  364. }
  365. break;
  366. case SLAPD_EXEMODE_DB2ARCHIVE:
  367. if (importing) {
  368. LDAPDebug(LDAP_DEBUG_ANY, NO_DB2BAK_DUE_TO_IMPORT, importing, 0, 0);
  369. result = -1;
  370. } else {
  371. add_this_process_to(export_dir);
  372. result = 0;
  373. }
  374. break;
  375. case SLAPD_EXEMODE_ARCHIVE2DB:
  376. case SLAPD_EXEMODE_LDIF2DB:
  377. if (running || importing || exporting) {
  378. LDAPDebug(LDAP_DEBUG_ANY, NO_IMPORT_DUE_TO_USE, 0, 0, 0);
  379. result = -1;
  380. } else {
  381. add_this_process_to(import_dir);
  382. result = 0;
  383. }
  384. break;
  385. case SLAPD_EXEMODE_DB2INDEX:
  386. if (running || importing || exporting) {
  387. LDAPDebug(LDAP_DEBUG_ANY, NO_DB2INDEX_DUE_TO_USE, 0, 0, 0);
  388. result = -1;
  389. } else {
  390. add_this_process_to(import_dir);
  391. result = 0;
  392. }
  393. break;
  394. case SLAPD_EXEMODE_UPGRADEDB:
  395. if (running || importing || exporting) {
  396. LDAPDebug(LDAP_DEBUG_ANY, NO_UPGRADEDB_DUE_TO_USE, 0, 0, 0);
  397. result = -1;
  398. } else {
  399. add_this_process_to(import_dir);
  400. result = 0;
  401. }
  402. break;
  403. case SLAPD_EXEMODE_UPGRADEDNFORMAT:
  404. if (running || importing || exporting) {
  405. LDAPDebug(LDAP_DEBUG_ANY, NO_UPGRADEDNFORMAT_DUE_TO_USE, 0, 0, 0);
  406. result = -1;
  407. } else {
  408. add_this_process_to(import_dir);
  409. result = 0;
  410. }
  411. break;
  412. case SLAPD_EXEMODE_DBTEST:
  413. if (running || importing || exporting) {
  414. LDAPDebug(LDAP_DEBUG_ANY, NO_DBTEST_DUE_TO_USE, 0, 0, 0);
  415. result = -1;
  416. } else {
  417. add_this_process_to(import_dir);
  418. result = 0;
  419. }
  420. break;
  421. }
  422. release_lockfile();
  423. if (result == 0) {
  424. atexit(remove_slapd_process);
  425. }
  426. return result;
  427. }
  428. /* is_slapd_running()
  429. * returns 1 if slapd is running, 0 if not, -1 on error
  430. */
  431. int
  432. is_slapd_running() {
  433. char server_dir[MAXPATHLEN];
  434. slapdFrontendConfig_t *cfg = getFrontendConfig();
  435. int running = 0;
  436. snprintf(server_dir, sizeof(server_dir), "%s/%s", cfg->lockdir, SERVER_DIR);
  437. server_dir[sizeof(server_dir)-1] = (char)0;
  438. /* Grab the lockfile */
  439. if (grab_lockfile() != 0) {
  440. /* Unable to grab the lockfile */
  441. return -1;
  442. }
  443. /* Make sure the directories exist */
  444. if (make_sure_dir_exists(cfg->lockdir) != 0 ||
  445. make_sure_dir_exists(server_dir) != 0) {
  446. release_lockfile();
  447. return -1;
  448. }
  449. running = sample_and_update(server_dir);
  450. release_lockfile();
  451. return running;
  452. }