PageRenderTime 39ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/src/bin/pg_basebackup/pg_receivexlog.c

https://github.com/larkly/postgres-docker
C | 506 lines | 361 code | 53 blank | 92 comment | 74 complexity | 588cb5c1745f0929fa391d8ece6af310 MD5 | raw file
Possible License(s): AGPL-3.0
  1. /*-------------------------------------------------------------------------
  2. *
  3. * pg_receivexlog.c - receive streaming transaction log data and write it
  4. * to a local file.
  5. *
  6. * Author: Magnus Hagander <magnus@hagander.net>
  7. *
  8. * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
  9. *
  10. * IDENTIFICATION
  11. * src/bin/pg_basebackup/pg_receivexlog.c
  12. *-------------------------------------------------------------------------
  13. */
  14. #include "postgres_fe.h"
  15. #include <dirent.h>
  16. #include <signal.h>
  17. #include <sys/stat.h>
  18. #include <sys/types.h>
  19. #include <unistd.h>
  20. #include "libpq-fe.h"
  21. #include "access/xlog_internal.h"
  22. #include "getopt_long.h"
  23. #include "receivelog.h"
  24. #include "streamutil.h"
  25. /* Time to sleep between reconnection attempts */
  26. #define RECONNECT_SLEEP_TIME 5
  27. /* Global options */
  28. static char *basedir = NULL;
  29. static int verbose = 0;
  30. static int noloop = 0;
  31. static int standby_message_timeout = 10 * 1000; /* 10 sec = default */
  32. static volatile bool time_to_abort = false;
  33. static void usage(void);
  34. static XLogRecPtr FindStreamingStart(uint32 *tli);
  35. static void StreamLog();
  36. static bool stop_streaming(XLogRecPtr segendpos, uint32 timeline,
  37. bool segment_finished);
  38. #define disconnect_and_exit(code) \
  39. { \
  40. if (conn != NULL) PQfinish(conn); \
  41. exit(code); \
  42. }
  43. static void
  44. usage(void)
  45. {
  46. printf(_("%s receives PostgreSQL streaming transaction logs.\n\n"),
  47. progname);
  48. printf(_("Usage:\n"));
  49. printf(_(" %s [OPTION]...\n"), progname);
  50. printf(_("\nOptions:\n"));
  51. printf(_(" -D, --directory=DIR receive transaction log files into this directory\n"));
  52. printf(_(" -n, --no-loop do not loop on connection lost\n"));
  53. printf(_(" -v, --verbose output verbose messages\n"));
  54. printf(_(" -V, --version output version information, then exit\n"));
  55. printf(_(" -?, --help show this help, then exit\n"));
  56. printf(_("\nConnection options:\n"));
  57. printf(_(" -d, --dbname=CONNSTR connection string\n"));
  58. printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
  59. printf(_(" -p, --port=PORT database server port number\n"));
  60. printf(_(" -s, --status-interval=INTERVAL\n"
  61. " time between status packets sent to server (in seconds)\n"));
  62. printf(_(" -U, --username=NAME connect as specified database user\n"));
  63. printf(_(" -w, --no-password never prompt for password\n"));
  64. printf(_(" -W, --password force password prompt (should happen automatically)\n"));
  65. printf(_(" --slot=SLOTNAME replication slot to use\n"));
  66. printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
  67. }
  68. static bool
  69. stop_streaming(XLogRecPtr xlogpos, uint32 timeline, bool segment_finished)
  70. {
  71. static uint32 prevtimeline = 0;
  72. static XLogRecPtr prevpos = InvalidXLogRecPtr;
  73. /* we assume that we get called once at the end of each segment */
  74. if (verbose && segment_finished)
  75. fprintf(stderr, _("%s: finished segment at %X/%X (timeline %u)\n"),
  76. progname, (uint32) (xlogpos >> 32), (uint32) xlogpos,
  77. timeline);
  78. /*
  79. * Note that we report the previous, not current, position here. After a
  80. * timeline switch, xlogpos points to the beginning of the segment because
  81. * that's where we always begin streaming. Reporting the end of previous
  82. * timeline isn't totally accurate, because the next timeline can begin
  83. * slightly before the end of the WAL that we received on the previous
  84. * timeline, but it's close enough for reporting purposes.
  85. */
  86. if (prevtimeline != 0 && prevtimeline != timeline)
  87. fprintf(stderr, _("%s: switched to timeline %u at %X/%X\n"),
  88. progname, timeline,
  89. (uint32) (prevpos >> 32), (uint32) prevpos);
  90. prevtimeline = timeline;
  91. prevpos = xlogpos;
  92. if (time_to_abort)
  93. {
  94. fprintf(stderr, _("%s: received interrupt signal, exiting\n"),
  95. progname);
  96. return true;
  97. }
  98. return false;
  99. }
  100. /*
  101. * Determine starting location for streaming, based on any existing xlog
  102. * segments in the directory. We start at the end of the last one that is
  103. * complete (size matches XLogSegSize), on the timeline with highest ID.
  104. *
  105. * If there are no WAL files in the directory, returns InvalidXLogRecPtr.
  106. */
  107. static XLogRecPtr
  108. FindStreamingStart(uint32 *tli)
  109. {
  110. DIR *dir;
  111. struct dirent *dirent;
  112. XLogSegNo high_segno = 0;
  113. uint32 high_tli = 0;
  114. bool high_ispartial = false;
  115. dir = opendir(basedir);
  116. if (dir == NULL)
  117. {
  118. fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
  119. progname, basedir, strerror(errno));
  120. disconnect_and_exit(1);
  121. }
  122. while (errno = 0, (dirent = readdir(dir)) != NULL)
  123. {
  124. uint32 tli;
  125. XLogSegNo segno;
  126. bool ispartial;
  127. /*
  128. * Check if the filename looks like an xlog file, or a .partial file.
  129. * Xlog files are always 24 characters, and .partial files are 32
  130. * characters.
  131. */
  132. if (strlen(dirent->d_name) == 24)
  133. {
  134. if (strspn(dirent->d_name, "0123456789ABCDEF") != 24)
  135. continue;
  136. ispartial = false;
  137. }
  138. else if (strlen(dirent->d_name) == 32)
  139. {
  140. if (strspn(dirent->d_name, "0123456789ABCDEF") != 24)
  141. continue;
  142. if (strcmp(&dirent->d_name[24], ".partial") != 0)
  143. continue;
  144. ispartial = true;
  145. }
  146. else
  147. continue;
  148. /*
  149. * Looks like an xlog file. Parse its position.
  150. */
  151. XLogFromFileName(dirent->d_name, &tli, &segno);
  152. /*
  153. * Check that the segment has the right size, if it's supposed to be
  154. * completed.
  155. */
  156. if (!ispartial)
  157. {
  158. struct stat statbuf;
  159. char fullpath[MAXPGPATH];
  160. snprintf(fullpath, sizeof(fullpath), "%s/%s", basedir, dirent->d_name);
  161. if (stat(fullpath, &statbuf) != 0)
  162. {
  163. fprintf(stderr, _("%s: could not stat file \"%s\": %s\n"),
  164. progname, fullpath, strerror(errno));
  165. disconnect_and_exit(1);
  166. }
  167. if (statbuf.st_size != XLOG_SEG_SIZE)
  168. {
  169. fprintf(stderr,
  170. _("%s: segment file \"%s\" has incorrect size %d, skipping\n"),
  171. progname, dirent->d_name, (int) statbuf.st_size);
  172. continue;
  173. }
  174. }
  175. /* Looks like a valid segment. Remember that we saw it. */
  176. if ((segno > high_segno) ||
  177. (segno == high_segno && tli > high_tli) ||
  178. (segno == high_segno && tli == high_tli && high_ispartial && !ispartial))
  179. {
  180. high_segno = segno;
  181. high_tli = tli;
  182. high_ispartial = ispartial;
  183. }
  184. }
  185. if (errno)
  186. {
  187. fprintf(stderr, _("%s: could not read directory \"%s\": %s\n"),
  188. progname, basedir, strerror(errno));
  189. disconnect_and_exit(1);
  190. }
  191. if (closedir(dir))
  192. {
  193. fprintf(stderr, _("%s: could not close directory \"%s\": %s\n"),
  194. progname, basedir, strerror(errno));
  195. disconnect_and_exit(1);
  196. }
  197. if (high_segno > 0)
  198. {
  199. XLogRecPtr high_ptr;
  200. /*
  201. * Move the starting pointer to the start of the next segment, if the
  202. * highest one we saw was completed. Otherwise start streaming from
  203. * the beginning of the .partial segment.
  204. */
  205. if (!high_ispartial)
  206. high_segno++;
  207. XLogSegNoOffsetToRecPtr(high_segno, 0, high_ptr);
  208. *tli = high_tli;
  209. return high_ptr;
  210. }
  211. else
  212. return InvalidXLogRecPtr;
  213. }
  214. /*
  215. * Start the log streaming
  216. */
  217. static void
  218. StreamLog(void)
  219. {
  220. PGresult *res;
  221. XLogRecPtr startpos;
  222. uint32 starttli;
  223. XLogRecPtr serverpos;
  224. uint32 servertli;
  225. uint32 hi,
  226. lo;
  227. /*
  228. * Connect in replication mode to the server
  229. */
  230. conn = GetConnection();
  231. if (!conn)
  232. /* Error message already written in GetConnection() */
  233. return;
  234. if (!CheckServerVersionForStreaming(conn))
  235. {
  236. /*
  237. * Error message already written in CheckServerVersionForStreaming().
  238. * There's no hope of recovering from a version mismatch, so don't
  239. * retry.
  240. */
  241. disconnect_and_exit(1);
  242. }
  243. /*
  244. * Run IDENTIFY_SYSTEM so we can get the timeline and current xlog
  245. * position.
  246. */
  247. res = PQexec(conn, "IDENTIFY_SYSTEM");
  248. if (PQresultStatus(res) != PGRES_TUPLES_OK)
  249. {
  250. fprintf(stderr, _("%s: could not send replication command \"%s\": %s"),
  251. progname, "IDENTIFY_SYSTEM", PQerrorMessage(conn));
  252. disconnect_and_exit(1);
  253. }
  254. if (PQntuples(res) != 1 || PQnfields(res) < 3)
  255. {
  256. fprintf(stderr,
  257. _("%s: could not identify system: got %d rows and %d fields, expected %d rows and %d or more fields\n"),
  258. progname, PQntuples(res), PQnfields(res), 1, 3);
  259. disconnect_and_exit(1);
  260. }
  261. servertli = atoi(PQgetvalue(res, 0, 1));
  262. if (sscanf(PQgetvalue(res, 0, 2), "%X/%X", &hi, &lo) != 2)
  263. {
  264. fprintf(stderr,
  265. _("%s: could not parse transaction log location \"%s\"\n"),
  266. progname, PQgetvalue(res, 0, 2));
  267. disconnect_and_exit(1);
  268. }
  269. serverpos = ((uint64) hi) << 32 | lo;
  270. PQclear(res);
  271. /*
  272. * Figure out where to start streaming.
  273. */
  274. startpos = FindStreamingStart(&starttli);
  275. if (startpos == InvalidXLogRecPtr)
  276. {
  277. startpos = serverpos;
  278. starttli = servertli;
  279. }
  280. /*
  281. * Always start streaming at the beginning of a segment
  282. */
  283. startpos -= startpos % XLOG_SEG_SIZE;
  284. /*
  285. * Start the replication
  286. */
  287. if (verbose)
  288. fprintf(stderr,
  289. _("%s: starting log streaming at %X/%X (timeline %u)\n"),
  290. progname, (uint32) (startpos >> 32), (uint32) startpos,
  291. starttli);
  292. ReceiveXlogStream(conn, startpos, starttli, NULL, basedir,
  293. stop_streaming, standby_message_timeout, ".partial");
  294. PQfinish(conn);
  295. }
  296. /*
  297. * When sigint is called, just tell the system to exit at the next possible
  298. * moment.
  299. */
  300. #ifndef WIN32
  301. static void
  302. sigint_handler(int signum)
  303. {
  304. time_to_abort = true;
  305. }
  306. #endif
  307. int
  308. main(int argc, char **argv)
  309. {
  310. static struct option long_options[] = {
  311. {"help", no_argument, NULL, '?'},
  312. {"version", no_argument, NULL, 'V'},
  313. {"directory", required_argument, NULL, 'D'},
  314. {"dbname", required_argument, NULL, 'd'},
  315. {"host", required_argument, NULL, 'h'},
  316. {"port", required_argument, NULL, 'p'},
  317. {"username", required_argument, NULL, 'U'},
  318. {"no-loop", no_argument, NULL, 'n'},
  319. {"no-password", no_argument, NULL, 'w'},
  320. {"password", no_argument, NULL, 'W'},
  321. {"status-interval", required_argument, NULL, 's'},
  322. {"slot", required_argument, NULL, 'S'},
  323. {"verbose", no_argument, NULL, 'v'},
  324. {NULL, 0, NULL, 0}
  325. };
  326. int c;
  327. int option_index;
  328. progname = get_progname(argv[0]);
  329. set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_receivexlog"));
  330. if (argc > 1)
  331. {
  332. if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
  333. {
  334. usage();
  335. exit(0);
  336. }
  337. else if (strcmp(argv[1], "-V") == 0 ||
  338. strcmp(argv[1], "--version") == 0)
  339. {
  340. puts("pg_receivexlog (PostgreSQL) " PG_VERSION);
  341. exit(0);
  342. }
  343. }
  344. while ((c = getopt_long(argc, argv, "D:d:h:p:U:s:nwWv",
  345. long_options, &option_index)) != -1)
  346. {
  347. switch (c)
  348. {
  349. case 'D':
  350. basedir = pg_strdup(optarg);
  351. break;
  352. case 'd':
  353. connection_string = pg_strdup(optarg);
  354. break;
  355. case 'h':
  356. dbhost = pg_strdup(optarg);
  357. break;
  358. case 'p':
  359. if (atoi(optarg) <= 0)
  360. {
  361. fprintf(stderr, _("%s: invalid port number \"%s\"\n"),
  362. progname, optarg);
  363. exit(1);
  364. }
  365. dbport = pg_strdup(optarg);
  366. break;
  367. case 'U':
  368. dbuser = pg_strdup(optarg);
  369. break;
  370. case 'w':
  371. dbgetpassword = -1;
  372. break;
  373. case 'W':
  374. dbgetpassword = 1;
  375. break;
  376. case 's':
  377. standby_message_timeout = atoi(optarg) * 1000;
  378. if (standby_message_timeout < 0)
  379. {
  380. fprintf(stderr, _("%s: invalid status interval \"%s\"\n"),
  381. progname, optarg);
  382. exit(1);
  383. }
  384. break;
  385. case 'S':
  386. replication_slot = pg_strdup(optarg);
  387. break;
  388. case 'n':
  389. noloop = 1;
  390. break;
  391. case 'v':
  392. verbose++;
  393. break;
  394. default:
  395. /*
  396. * getopt_long already emitted a complaint
  397. */
  398. fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
  399. progname);
  400. exit(1);
  401. }
  402. }
  403. /*
  404. * Any non-option arguments?
  405. */
  406. if (optind < argc)
  407. {
  408. fprintf(stderr,
  409. _("%s: too many command-line arguments (first is \"%s\")\n"),
  410. progname, argv[optind]);
  411. fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
  412. progname);
  413. exit(1);
  414. }
  415. /*
  416. * Required arguments
  417. */
  418. if (basedir == NULL)
  419. {
  420. fprintf(stderr, _("%s: no target directory specified\n"), progname);
  421. fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
  422. progname);
  423. exit(1);
  424. }
  425. #ifndef WIN32
  426. pqsignal(SIGINT, sigint_handler);
  427. #endif
  428. while (true)
  429. {
  430. StreamLog();
  431. if (time_to_abort)
  432. {
  433. /*
  434. * We've been Ctrl-C'ed. That's not an error, so exit without an
  435. * errorcode.
  436. */
  437. exit(0);
  438. }
  439. else if (noloop)
  440. {
  441. fprintf(stderr, _("%s: disconnected\n"), progname);
  442. exit(1);
  443. }
  444. else
  445. {
  446. fprintf(stderr,
  447. /* translator: check source for value for %d */
  448. _("%s: disconnected; waiting %d seconds to try again\n"),
  449. progname, RECONNECT_SLEEP_TIME);
  450. pg_usleep(RECONNECT_SLEEP_TIME * 1000000);
  451. }
  452. }
  453. }