PageRenderTime 91ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/src/bin/pg_upgrade/option.c

https://bitbucket.org/adunstan/pgdevel
C | 520 lines | 355 code | 70 blank | 95 comment | 69 complexity | d978d8c27746433b36457fc3d71be6d8 MD5 | raw file
Possible License(s): AGPL-3.0
  1. /*
  2. * opt.c
  3. *
  4. * options functions
  5. *
  6. * Copyright (c) 2010-2018, PostgreSQL Global Development Group
  7. * src/bin/pg_upgrade/option.c
  8. */
  9. #include "postgres_fe.h"
  10. #include <time.h>
  11. #ifdef WIN32
  12. #include <io.h>
  13. #endif
  14. #include "getopt_long.h"
  15. #include "utils/pidfile.h"
  16. #include "pg_upgrade.h"
  17. static void usage(void);
  18. static void check_required_directory(char **dirpath, char **configpath,
  19. const char *envVarName, const char *cmdLineOption, const char *description);
  20. #define FIX_DEFAULT_READ_ONLY "-c default_transaction_read_only=false"
  21. UserOpts user_opts;
  22. /*
  23. * parseCommandLine()
  24. *
  25. * Parses the command line (argc, argv[]) and loads structures
  26. */
  27. void
  28. parseCommandLine(int argc, char *argv[])
  29. {
  30. static struct option long_options[] = {
  31. {"old-datadir", required_argument, NULL, 'd'},
  32. {"new-datadir", required_argument, NULL, 'D'},
  33. {"old-bindir", required_argument, NULL, 'b'},
  34. {"new-bindir", required_argument, NULL, 'B'},
  35. {"old-options", required_argument, NULL, 'o'},
  36. {"new-options", required_argument, NULL, 'O'},
  37. {"old-port", required_argument, NULL, 'p'},
  38. {"new-port", required_argument, NULL, 'P'},
  39. {"username", required_argument, NULL, 'U'},
  40. {"check", no_argument, NULL, 'c'},
  41. {"link", no_argument, NULL, 'k'},
  42. {"retain", no_argument, NULL, 'r'},
  43. {"jobs", required_argument, NULL, 'j'},
  44. {"verbose", no_argument, NULL, 'v'},
  45. {NULL, 0, NULL, 0}
  46. };
  47. int option; /* Command line option */
  48. int optindex = 0; /* used by getopt_long */
  49. int os_user_effective_id;
  50. FILE *fp;
  51. char **filename;
  52. time_t run_time = time(NULL);
  53. user_opts.transfer_mode = TRANSFER_MODE_COPY;
  54. os_info.progname = get_progname(argv[0]);
  55. /* Process libpq env. variables; load values here for usage() output */
  56. old_cluster.port = getenv("PGPORTOLD") ? atoi(getenv("PGPORTOLD")) : DEF_PGUPORT;
  57. new_cluster.port = getenv("PGPORTNEW") ? atoi(getenv("PGPORTNEW")) : DEF_PGUPORT;
  58. os_user_effective_id = get_user_info(&os_info.user);
  59. /* we override just the database user name; we got the OS id above */
  60. if (getenv("PGUSER"))
  61. {
  62. pg_free(os_info.user);
  63. /* must save value, getenv()'s pointer is not stable */
  64. os_info.user = pg_strdup(getenv("PGUSER"));
  65. }
  66. if (argc > 1)
  67. {
  68. if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
  69. {
  70. usage();
  71. exit(0);
  72. }
  73. if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
  74. {
  75. puts("pg_upgrade (PostgreSQL) " PG_VERSION);
  76. exit(0);
  77. }
  78. }
  79. /* Allow help and version to be run as root, so do the test here. */
  80. if (os_user_effective_id == 0)
  81. pg_fatal("%s: cannot be run as root\n", os_info.progname);
  82. if ((log_opts.internal = fopen_priv(INTERNAL_LOG_FILE, "a")) == NULL)
  83. pg_fatal("could not write to log file \"%s\"\n", INTERNAL_LOG_FILE);
  84. while ((option = getopt_long(argc, argv, "d:D:b:B:cj:ko:O:p:P:rU:v",
  85. long_options, &optindex)) != -1)
  86. {
  87. switch (option)
  88. {
  89. case 'b':
  90. old_cluster.bindir = pg_strdup(optarg);
  91. break;
  92. case 'B':
  93. new_cluster.bindir = pg_strdup(optarg);
  94. break;
  95. case 'c':
  96. user_opts.check = true;
  97. break;
  98. case 'd':
  99. old_cluster.pgdata = pg_strdup(optarg);
  100. old_cluster.pgconfig = pg_strdup(optarg);
  101. break;
  102. case 'D':
  103. new_cluster.pgdata = pg_strdup(optarg);
  104. new_cluster.pgconfig = pg_strdup(optarg);
  105. break;
  106. case 'j':
  107. user_opts.jobs = atoi(optarg);
  108. break;
  109. case 'k':
  110. user_opts.transfer_mode = TRANSFER_MODE_LINK;
  111. break;
  112. case 'o':
  113. /* append option? */
  114. if (!old_cluster.pgopts)
  115. old_cluster.pgopts = pg_strdup(optarg);
  116. else
  117. {
  118. char *old_pgopts = old_cluster.pgopts;
  119. old_cluster.pgopts = psprintf("%s %s", old_pgopts, optarg);
  120. free(old_pgopts);
  121. }
  122. break;
  123. case 'O':
  124. /* append option? */
  125. if (!new_cluster.pgopts)
  126. new_cluster.pgopts = pg_strdup(optarg);
  127. else
  128. {
  129. char *new_pgopts = new_cluster.pgopts;
  130. new_cluster.pgopts = psprintf("%s %s", new_pgopts, optarg);
  131. free(new_pgopts);
  132. }
  133. break;
  134. /*
  135. * Someday, the port number option could be removed and passed
  136. * using -o/-O, but that requires postmaster -C to be
  137. * supported on all old/new versions (added in PG 9.2).
  138. */
  139. case 'p':
  140. if ((old_cluster.port = atoi(optarg)) <= 0)
  141. {
  142. pg_fatal("invalid old port number\n");
  143. exit(1);
  144. }
  145. break;
  146. case 'P':
  147. if ((new_cluster.port = atoi(optarg)) <= 0)
  148. {
  149. pg_fatal("invalid new port number\n");
  150. exit(1);
  151. }
  152. break;
  153. case 'r':
  154. log_opts.retain = true;
  155. break;
  156. case 'U':
  157. pg_free(os_info.user);
  158. os_info.user = pg_strdup(optarg);
  159. os_info.user_specified = true;
  160. /*
  161. * Push the user name into the environment so pre-9.1
  162. * pg_ctl/libpq uses it.
  163. */
  164. pg_putenv("PGUSER", os_info.user);
  165. break;
  166. case 'v':
  167. pg_log(PG_REPORT, "Running in verbose mode\n");
  168. log_opts.verbose = true;
  169. break;
  170. default:
  171. pg_fatal("Try \"%s --help\" for more information.\n",
  172. os_info.progname);
  173. break;
  174. }
  175. }
  176. /* label start of upgrade in logfiles */
  177. for (filename = output_files; *filename != NULL; filename++)
  178. {
  179. if ((fp = fopen_priv(*filename, "a")) == NULL)
  180. pg_fatal("could not write to log file \"%s\"\n", *filename);
  181. /* Start with newline because we might be appending to a file. */
  182. fprintf(fp, "\n"
  183. "-----------------------------------------------------------------\n"
  184. " pg_upgrade run on %s"
  185. "-----------------------------------------------------------------\n\n",
  186. ctime(&run_time));
  187. fclose(fp);
  188. }
  189. /* Turn off read-only mode; add prefix to PGOPTIONS? */
  190. if (getenv("PGOPTIONS"))
  191. {
  192. char *pgoptions = psprintf("%s %s", FIX_DEFAULT_READ_ONLY,
  193. getenv("PGOPTIONS"));
  194. pg_putenv("PGOPTIONS", pgoptions);
  195. pfree(pgoptions);
  196. }
  197. else
  198. pg_putenv("PGOPTIONS", FIX_DEFAULT_READ_ONLY);
  199. /* Get values from env if not already set */
  200. check_required_directory(&old_cluster.bindir, NULL, "PGBINOLD", "-b",
  201. _("old cluster binaries reside"));
  202. check_required_directory(&new_cluster.bindir, NULL, "PGBINNEW", "-B",
  203. _("new cluster binaries reside"));
  204. check_required_directory(&old_cluster.pgdata, &old_cluster.pgconfig,
  205. "PGDATAOLD", "-d", _("old cluster data resides"));
  206. check_required_directory(&new_cluster.pgdata, &new_cluster.pgconfig,
  207. "PGDATANEW", "-D", _("new cluster data resides"));
  208. #ifdef WIN32
  209. /*
  210. * On Windows, initdb --sync-only will fail with a "Permission denied"
  211. * error on file pg_upgrade_utility.log if pg_upgrade is run inside the
  212. * new cluster directory, so we do a check here.
  213. */
  214. {
  215. char cwd[MAXPGPATH],
  216. new_cluster_pgdata[MAXPGPATH];
  217. strlcpy(new_cluster_pgdata, new_cluster.pgdata, MAXPGPATH);
  218. canonicalize_path(new_cluster_pgdata);
  219. if (!getcwd(cwd, MAXPGPATH))
  220. pg_fatal("could not determine current directory\n");
  221. canonicalize_path(cwd);
  222. if (path_is_prefix_of_path(new_cluster_pgdata, cwd))
  223. pg_fatal("cannot run pg_upgrade from inside the new cluster data directory on Windows\n");
  224. }
  225. #endif
  226. }
  227. static void
  228. usage(void)
  229. {
  230. printf(_("pg_upgrade upgrades a PostgreSQL cluster to a different major version.\n\n"));
  231. printf(_("Usage:\n"));
  232. printf(_(" pg_upgrade [OPTION]...\n\n"));
  233. printf(_("Options:\n"));
  234. printf(_(" -b, --old-bindir=BINDIR old cluster executable directory\n"));
  235. printf(_(" -B, --new-bindir=BINDIR new cluster executable directory\n"));
  236. printf(_(" -c, --check check clusters only, don't change any data\n"));
  237. printf(_(" -d, --old-datadir=DATADIR old cluster data directory\n"));
  238. printf(_(" -D, --new-datadir=DATADIR new cluster data directory\n"));
  239. printf(_(" -j, --jobs number of simultaneous processes or threads to use\n"));
  240. printf(_(" -k, --link link instead of copying files to new cluster\n"));
  241. printf(_(" -o, --old-options=OPTIONS old cluster options to pass to the server\n"));
  242. printf(_(" -O, --new-options=OPTIONS new cluster options to pass to the server\n"));
  243. printf(_(" -p, --old-port=PORT old cluster port number (default %d)\n"), old_cluster.port);
  244. printf(_(" -P, --new-port=PORT new cluster port number (default %d)\n"), new_cluster.port);
  245. printf(_(" -r, --retain retain SQL and log files after success\n"));
  246. printf(_(" -U, --username=NAME cluster superuser (default \"%s\")\n"), os_info.user);
  247. printf(_(" -v, --verbose enable verbose internal logging\n"));
  248. printf(_(" -V, --version display version information, then exit\n"));
  249. printf(_(" -?, --help show this help, then exit\n"));
  250. printf(_("\n"
  251. "Before running pg_upgrade you must:\n"
  252. " create a new database cluster (using the new version of initdb)\n"
  253. " shutdown the postmaster servicing the old cluster\n"
  254. " shutdown the postmaster servicing the new cluster\n"));
  255. printf(_("\n"
  256. "When you run pg_upgrade, you must provide the following information:\n"
  257. " the data directory for the old cluster (-d DATADIR)\n"
  258. " the data directory for the new cluster (-D DATADIR)\n"
  259. " the \"bin\" directory for the old version (-b BINDIR)\n"
  260. " the \"bin\" directory for the new version (-B BINDIR)\n"));
  261. printf(_("\n"
  262. "For example:\n"
  263. " pg_upgrade -d oldCluster/data -D newCluster/data -b oldCluster/bin -B newCluster/bin\n"
  264. "or\n"));
  265. #ifndef WIN32
  266. printf(_(" $ export PGDATAOLD=oldCluster/data\n"
  267. " $ export PGDATANEW=newCluster/data\n"
  268. " $ export PGBINOLD=oldCluster/bin\n"
  269. " $ export PGBINNEW=newCluster/bin\n"
  270. " $ pg_upgrade\n"));
  271. #else
  272. printf(_(" C:\\> set PGDATAOLD=oldCluster/data\n"
  273. " C:\\> set PGDATANEW=newCluster/data\n"
  274. " C:\\> set PGBINOLD=oldCluster/bin\n"
  275. " C:\\> set PGBINNEW=newCluster/bin\n"
  276. " C:\\> pg_upgrade\n"));
  277. #endif
  278. printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
  279. }
  280. /*
  281. * check_required_directory()
  282. *
  283. * Checks a directory option.
  284. * dirpath - the directory name supplied on the command line
  285. * configpath - optional configuration directory
  286. * envVarName - the name of an environment variable to get if dirpath is NULL
  287. * cmdLineOption - the command line option corresponds to this directory (-o, -O, -n, -N)
  288. * description - a description of this directory option
  289. *
  290. * We use the last two arguments to construct a meaningful error message if the
  291. * user hasn't provided the required directory name.
  292. */
  293. static void
  294. check_required_directory(char **dirpath, char **configpath,
  295. const char *envVarName, const char *cmdLineOption,
  296. const char *description)
  297. {
  298. if (*dirpath == NULL || strlen(*dirpath) == 0)
  299. {
  300. const char *envVar;
  301. if ((envVar = getenv(envVarName)) && strlen(envVar))
  302. {
  303. *dirpath = pg_strdup(envVar);
  304. if (configpath)
  305. *configpath = pg_strdup(envVar);
  306. }
  307. else
  308. pg_fatal("You must identify the directory where the %s.\n"
  309. "Please use the %s command-line option or the %s environment variable.\n",
  310. description, cmdLineOption, envVarName);
  311. }
  312. /*
  313. * Trim off any trailing path separators because we construct paths by
  314. * appending to this path.
  315. */
  316. #ifndef WIN32
  317. if ((*dirpath)[strlen(*dirpath) - 1] == '/')
  318. #else
  319. if ((*dirpath)[strlen(*dirpath) - 1] == '/' ||
  320. (*dirpath)[strlen(*dirpath) - 1] == '\\')
  321. #endif
  322. (*dirpath)[strlen(*dirpath) - 1] = 0;
  323. }
  324. /*
  325. * adjust_data_dir
  326. *
  327. * If a configuration-only directory was specified, find the real data dir
  328. * by querying the running server. This has limited checking because we
  329. * can't check for a running server because we can't find postmaster.pid.
  330. */
  331. void
  332. adjust_data_dir(ClusterInfo *cluster)
  333. {
  334. char filename[MAXPGPATH];
  335. char cmd[MAXPGPATH],
  336. cmd_output[MAX_STRING];
  337. FILE *fp,
  338. *output;
  339. /* If there is no postgresql.conf, it can't be a config-only dir */
  340. snprintf(filename, sizeof(filename), "%s/postgresql.conf", cluster->pgconfig);
  341. if ((fp = fopen(filename, "r")) == NULL)
  342. return;
  343. fclose(fp);
  344. /* If PG_VERSION exists, it can't be a config-only dir */
  345. snprintf(filename, sizeof(filename), "%s/PG_VERSION", cluster->pgconfig);
  346. if ((fp = fopen(filename, "r")) != NULL)
  347. {
  348. fclose(fp);
  349. return;
  350. }
  351. /* Must be a configuration directory, so find the real data directory. */
  352. if (cluster == &old_cluster)
  353. prep_status("Finding the real data directory for the source cluster");
  354. else
  355. prep_status("Finding the real data directory for the target cluster");
  356. /*
  357. * We don't have a data directory yet, so we can't check the PG version,
  358. * so this might fail --- only works for PG 9.2+. If this fails,
  359. * pg_upgrade will fail anyway because the data files will not be found.
  360. */
  361. snprintf(cmd, sizeof(cmd), "\"%s/postgres\" -D \"%s\" -C data_directory",
  362. cluster->bindir, cluster->pgconfig);
  363. if ((output = popen(cmd, "r")) == NULL ||
  364. fgets(cmd_output, sizeof(cmd_output), output) == NULL)
  365. pg_fatal("could not get data directory using %s: %s\n",
  366. cmd, strerror(errno));
  367. pclose(output);
  368. /* Remove trailing newline */
  369. if (strchr(cmd_output, '\n') != NULL)
  370. *strchr(cmd_output, '\n') = '\0';
  371. cluster->pgdata = pg_strdup(cmd_output);
  372. check_ok();
  373. }
  374. /*
  375. * get_sock_dir
  376. *
  377. * Identify the socket directory to use for this cluster. If we're doing
  378. * a live check (old cluster only), we need to find out where the postmaster
  379. * is listening. Otherwise, we're going to put the socket into the current
  380. * directory.
  381. */
  382. void
  383. get_sock_dir(ClusterInfo *cluster, bool live_check)
  384. {
  385. #ifdef HAVE_UNIX_SOCKETS
  386. /*
  387. * sockdir and port were added to postmaster.pid in PG 9.1. Pre-9.1 cannot
  388. * process pg_ctl -w for sockets in non-default locations.
  389. */
  390. if (GET_MAJOR_VERSION(cluster->major_version) >= 901)
  391. {
  392. if (!live_check)
  393. {
  394. /* Use the current directory for the socket */
  395. cluster->sockdir = pg_malloc(MAXPGPATH);
  396. if (!getcwd(cluster->sockdir, MAXPGPATH))
  397. pg_fatal("could not determine current directory\n");
  398. }
  399. else
  400. {
  401. /*
  402. * If we are doing a live check, we will use the old cluster's
  403. * Unix domain socket directory so we can connect to the live
  404. * server.
  405. */
  406. unsigned short orig_port = cluster->port;
  407. char filename[MAXPGPATH],
  408. line[MAXPGPATH];
  409. FILE *fp;
  410. int lineno;
  411. snprintf(filename, sizeof(filename), "%s/postmaster.pid",
  412. cluster->pgdata);
  413. if ((fp = fopen(filename, "r")) == NULL)
  414. pg_fatal("could not open file \"%s\": %s\n",
  415. filename, strerror(errno));
  416. for (lineno = 1;
  417. lineno <= Max(LOCK_FILE_LINE_PORT, LOCK_FILE_LINE_SOCKET_DIR);
  418. lineno++)
  419. {
  420. if (fgets(line, sizeof(line), fp) == NULL)
  421. pg_fatal("could not read line %d from file \"%s\": %s\n",
  422. lineno, filename, strerror(errno));
  423. /* potentially overwrite user-supplied value */
  424. if (lineno == LOCK_FILE_LINE_PORT)
  425. sscanf(line, "%hu", &old_cluster.port);
  426. if (lineno == LOCK_FILE_LINE_SOCKET_DIR)
  427. {
  428. cluster->sockdir = pg_strdup(line);
  429. /* strip off newline */
  430. if (strchr(cluster->sockdir, '\n') != NULL)
  431. *strchr(cluster->sockdir, '\n') = '\0';
  432. }
  433. }
  434. fclose(fp);
  435. /* warn of port number correction */
  436. if (orig_port != DEF_PGUPORT && old_cluster.port != orig_port)
  437. pg_log(PG_WARNING, "user-supplied old port number %hu corrected to %hu\n",
  438. orig_port, cluster->port);
  439. }
  440. }
  441. else
  442. /*
  443. * Can't get sockdir and pg_ctl -w can't use a non-default, use
  444. * default
  445. */
  446. cluster->sockdir = NULL;
  447. #else /* !HAVE_UNIX_SOCKETS */
  448. cluster->sockdir = NULL;
  449. #endif
  450. }