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

/src/bin/pg_basebackup/pg_recvlogical.c

https://bitbucket.org/adunstan/pgdevel
C | 1072 lines | 783 code | 129 blank | 160 comment | 174 complexity | d39396f71a409024a4d097c204583283 MD5 | raw file
Possible License(s): AGPL-3.0
  1. /*-------------------------------------------------------------------------
  2. *
  3. * pg_recvlogical.c - receive data from a logical decoding slot in a streaming
  4. * fashion and write it to a local file.
  5. *
  6. * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
  7. *
  8. * IDENTIFICATION
  9. * src/bin/pg_basebackup/pg_recvlogical.c
  10. *-------------------------------------------------------------------------
  11. */
  12. #include "postgres_fe.h"
  13. #include <dirent.h>
  14. #include <sys/stat.h>
  15. #include <unistd.h>
  16. #ifdef HAVE_SYS_SELECT_H
  17. #include <sys/select.h>
  18. #endif
  19. /* local includes */
  20. #include "streamutil.h"
  21. #include "access/xlog_internal.h"
  22. #include "common/file_perm.h"
  23. #include "common/fe_memutils.h"
  24. #include "getopt_long.h"
  25. #include "libpq-fe.h"
  26. #include "libpq/pqsignal.h"
  27. #include "pqexpbuffer.h"
  28. /* Time to sleep between reconnection attempts */
  29. #define RECONNECT_SLEEP_TIME 5
  30. /* Global Options */
  31. static char *outfile = NULL;
  32. static int verbose = 0;
  33. static int noloop = 0;
  34. static int standby_message_timeout = 10 * 1000; /* 10 sec = default */
  35. static int fsync_interval = 10 * 1000; /* 10 sec = default */
  36. static XLogRecPtr startpos = InvalidXLogRecPtr;
  37. static XLogRecPtr endpos = InvalidXLogRecPtr;
  38. static bool do_create_slot = false;
  39. static bool slot_exists_ok = false;
  40. static bool do_start_slot = false;
  41. static bool do_drop_slot = false;
  42. static char *replication_slot = NULL;
  43. /* filled pairwise with option, value. value may be NULL */
  44. static char **options;
  45. static size_t noptions = 0;
  46. static const char *plugin = "test_decoding";
  47. /* Global State */
  48. static int outfd = -1;
  49. static volatile sig_atomic_t time_to_abort = false;
  50. static volatile sig_atomic_t output_reopen = false;
  51. static bool output_isfile;
  52. static TimestampTz output_last_fsync = -1;
  53. static bool output_needs_fsync = false;
  54. static XLogRecPtr output_written_lsn = InvalidXLogRecPtr;
  55. static XLogRecPtr output_fsync_lsn = InvalidXLogRecPtr;
  56. static void usage(void);
  57. static void StreamLogicalLog(void);
  58. static void disconnect_and_exit(int code) pg_attribute_noreturn();
  59. static bool flushAndSendFeedback(PGconn *conn, TimestampTz *now);
  60. static void prepareToTerminate(PGconn *conn, XLogRecPtr endpos,
  61. bool keepalive, XLogRecPtr lsn);
  62. static void
  63. usage(void)
  64. {
  65. printf(_("%s controls PostgreSQL logical decoding streams.\n\n"),
  66. progname);
  67. printf(_("Usage:\n"));
  68. printf(_(" %s [OPTION]...\n"), progname);
  69. printf(_("\nAction to be performed:\n"));
  70. printf(_(" --create-slot create a new replication slot (for the slot's name see --slot)\n"));
  71. printf(_(" --drop-slot drop the replication slot (for the slot's name see --slot)\n"));
  72. printf(_(" --start start streaming in a replication slot (for the slot's name see --slot)\n"));
  73. printf(_("\nOptions:\n"));
  74. printf(_(" -E, --endpos=LSN exit after receiving the specified LSN\n"));
  75. printf(_(" -f, --file=FILE receive log into this file, - for stdout\n"));
  76. printf(_(" -F --fsync-interval=SECS\n"
  77. " time between fsyncs to the output file (default: %d)\n"), (fsync_interval / 1000));
  78. printf(_(" --if-not-exists do not error if slot already exists when creating a slot\n"));
  79. printf(_(" -I, --startpos=LSN where in an existing slot should the streaming start\n"));
  80. printf(_(" -n, --no-loop do not loop on connection lost\n"));
  81. printf(_(" -o, --option=NAME[=VALUE]\n"
  82. " pass option NAME with optional value VALUE to the\n"
  83. " output plugin\n"));
  84. printf(_(" -P, --plugin=PLUGIN use output plugin PLUGIN (default: %s)\n"), plugin);
  85. printf(_(" -s, --status-interval=SECS\n"
  86. " time between status packets sent to server (default: %d)\n"), (standby_message_timeout / 1000));
  87. printf(_(" -S, --slot=SLOTNAME name of the logical replication slot\n"));
  88. printf(_(" -v, --verbose output verbose messages\n"));
  89. printf(_(" -V, --version output version information, then exit\n"));
  90. printf(_(" -?, --help show this help, then exit\n"));
  91. printf(_("\nConnection options:\n"));
  92. printf(_(" -d, --dbname=DBNAME database to connect to\n"));
  93. printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
  94. printf(_(" -p, --port=PORT database server port number\n"));
  95. printf(_(" -U, --username=NAME connect as specified database user\n"));
  96. printf(_(" -w, --no-password never prompt for password\n"));
  97. printf(_(" -W, --password force password prompt (should happen automatically)\n"));
  98. printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
  99. }
  100. /*
  101. * Send a Standby Status Update message to server.
  102. */
  103. static bool
  104. sendFeedback(PGconn *conn, TimestampTz now, bool force, bool replyRequested)
  105. {
  106. static XLogRecPtr last_written_lsn = InvalidXLogRecPtr;
  107. static XLogRecPtr last_fsync_lsn = InvalidXLogRecPtr;
  108. char replybuf[1 + 8 + 8 + 8 + 8 + 1];
  109. int len = 0;
  110. /*
  111. * we normally don't want to send superfluous feedback, but if it's
  112. * because of a timeout we need to, otherwise wal_sender_timeout will kill
  113. * us.
  114. */
  115. if (!force &&
  116. last_written_lsn == output_written_lsn &&
  117. last_fsync_lsn != output_fsync_lsn)
  118. return true;
  119. if (verbose)
  120. fprintf(stderr,
  121. _("%s: confirming write up to %X/%X, flush to %X/%X (slot %s)\n"),
  122. progname,
  123. (uint32) (output_written_lsn >> 32), (uint32) output_written_lsn,
  124. (uint32) (output_fsync_lsn >> 32), (uint32) output_fsync_lsn,
  125. replication_slot);
  126. replybuf[len] = 'r';
  127. len += 1;
  128. fe_sendint64(output_written_lsn, &replybuf[len]); /* write */
  129. len += 8;
  130. fe_sendint64(output_fsync_lsn, &replybuf[len]); /* flush */
  131. len += 8;
  132. fe_sendint64(InvalidXLogRecPtr, &replybuf[len]); /* apply */
  133. len += 8;
  134. fe_sendint64(now, &replybuf[len]); /* sendTime */
  135. len += 8;
  136. replybuf[len] = replyRequested ? 1 : 0; /* replyRequested */
  137. len += 1;
  138. startpos = output_written_lsn;
  139. last_written_lsn = output_written_lsn;
  140. last_fsync_lsn = output_fsync_lsn;
  141. if (PQputCopyData(conn, replybuf, len) <= 0 || PQflush(conn))
  142. {
  143. fprintf(stderr, _("%s: could not send feedback packet: %s"),
  144. progname, PQerrorMessage(conn));
  145. return false;
  146. }
  147. return true;
  148. }
  149. static void
  150. disconnect_and_exit(int code)
  151. {
  152. if (conn != NULL)
  153. PQfinish(conn);
  154. exit(code);
  155. }
  156. static bool
  157. OutputFsync(TimestampTz now)
  158. {
  159. output_last_fsync = now;
  160. output_fsync_lsn = output_written_lsn;
  161. if (fsync_interval <= 0)
  162. return true;
  163. if (!output_needs_fsync)
  164. return true;
  165. output_needs_fsync = false;
  166. /* can only fsync if it's a regular file */
  167. if (!output_isfile)
  168. return true;
  169. if (fsync(outfd) != 0)
  170. {
  171. fprintf(stderr,
  172. _("%s: could not fsync log file \"%s\": %s\n"),
  173. progname, outfile, strerror(errno));
  174. return false;
  175. }
  176. return true;
  177. }
  178. /*
  179. * Start the log streaming
  180. */
  181. static void
  182. StreamLogicalLog(void)
  183. {
  184. PGresult *res;
  185. char *copybuf = NULL;
  186. TimestampTz last_status = -1;
  187. int i;
  188. PQExpBuffer query;
  189. output_written_lsn = InvalidXLogRecPtr;
  190. output_fsync_lsn = InvalidXLogRecPtr;
  191. query = createPQExpBuffer();
  192. /*
  193. * Connect in replication mode to the server
  194. */
  195. if (!conn)
  196. conn = GetConnection();
  197. if (!conn)
  198. /* Error message already written in GetConnection() */
  199. return;
  200. /*
  201. * Start the replication
  202. */
  203. if (verbose)
  204. fprintf(stderr,
  205. _("%s: starting log streaming at %X/%X (slot %s)\n"),
  206. progname, (uint32) (startpos >> 32), (uint32) startpos,
  207. replication_slot);
  208. /* Initiate the replication stream at specified location */
  209. appendPQExpBuffer(query, "START_REPLICATION SLOT \"%s\" LOGICAL %X/%X",
  210. replication_slot, (uint32) (startpos >> 32), (uint32) startpos);
  211. /* print options if there are any */
  212. if (noptions)
  213. appendPQExpBufferStr(query, " (");
  214. for (i = 0; i < noptions; i++)
  215. {
  216. /* separator */
  217. if (i > 0)
  218. appendPQExpBufferStr(query, ", ");
  219. /* write option name */
  220. appendPQExpBuffer(query, "\"%s\"", options[(i * 2)]);
  221. /* write option value if specified */
  222. if (options[(i * 2) + 1] != NULL)
  223. appendPQExpBuffer(query, " '%s'", options[(i * 2) + 1]);
  224. }
  225. if (noptions)
  226. appendPQExpBufferChar(query, ')');
  227. res = PQexec(conn, query->data);
  228. if (PQresultStatus(res) != PGRES_COPY_BOTH)
  229. {
  230. fprintf(stderr, _("%s: could not send replication command \"%s\": %s"),
  231. progname, query->data, PQresultErrorMessage(res));
  232. PQclear(res);
  233. goto error;
  234. }
  235. PQclear(res);
  236. resetPQExpBuffer(query);
  237. if (verbose)
  238. fprintf(stderr,
  239. _("%s: streaming initiated\n"),
  240. progname);
  241. while (!time_to_abort)
  242. {
  243. int r;
  244. int bytes_left;
  245. int bytes_written;
  246. TimestampTz now;
  247. int hdr_len;
  248. XLogRecPtr cur_record_lsn = InvalidXLogRecPtr;
  249. if (copybuf != NULL)
  250. {
  251. PQfreemem(copybuf);
  252. copybuf = NULL;
  253. }
  254. /*
  255. * Potentially send a status message to the master
  256. */
  257. now = feGetCurrentTimestamp();
  258. if (outfd != -1 &&
  259. feTimestampDifferenceExceeds(output_last_fsync, now,
  260. fsync_interval))
  261. {
  262. if (!OutputFsync(now))
  263. goto error;
  264. }
  265. if (standby_message_timeout > 0 &&
  266. feTimestampDifferenceExceeds(last_status, now,
  267. standby_message_timeout))
  268. {
  269. /* Time to send feedback! */
  270. if (!sendFeedback(conn, now, true, false))
  271. goto error;
  272. last_status = now;
  273. }
  274. /* got SIGHUP, close output file */
  275. if (outfd != -1 && output_reopen && strcmp(outfile, "-") != 0)
  276. {
  277. now = feGetCurrentTimestamp();
  278. if (!OutputFsync(now))
  279. goto error;
  280. close(outfd);
  281. outfd = -1;
  282. }
  283. output_reopen = false;
  284. /* open the output file, if not open yet */
  285. if (outfd == -1)
  286. {
  287. struct stat statbuf;
  288. if (strcmp(outfile, "-") == 0)
  289. outfd = fileno(stdout);
  290. else
  291. outfd = open(outfile, O_CREAT | O_APPEND | O_WRONLY | PG_BINARY,
  292. S_IRUSR | S_IWUSR);
  293. if (outfd == -1)
  294. {
  295. fprintf(stderr,
  296. _("%s: could not open log file \"%s\": %s\n"),
  297. progname, outfile, strerror(errno));
  298. goto error;
  299. }
  300. if (fstat(outfd, &statbuf) != 0)
  301. fprintf(stderr,
  302. _("%s: could not stat file \"%s\": %s\n"),
  303. progname, outfile, strerror(errno));
  304. output_isfile = S_ISREG(statbuf.st_mode) && !isatty(outfd);
  305. }
  306. r = PQgetCopyData(conn, &copybuf, 1);
  307. if (r == 0)
  308. {
  309. /*
  310. * In async mode, and no data available. We block on reading but
  311. * not more than the specified timeout, so that we can send a
  312. * response back to the client.
  313. */
  314. fd_set input_mask;
  315. TimestampTz message_target = 0;
  316. TimestampTz fsync_target = 0;
  317. struct timeval timeout;
  318. struct timeval *timeoutptr = NULL;
  319. if (PQsocket(conn) < 0)
  320. {
  321. fprintf(stderr,
  322. _("%s: invalid socket: %s"),
  323. progname, PQerrorMessage(conn));
  324. goto error;
  325. }
  326. FD_ZERO(&input_mask);
  327. FD_SET(PQsocket(conn), &input_mask);
  328. /* Compute when we need to wakeup to send a keepalive message. */
  329. if (standby_message_timeout)
  330. message_target = last_status + (standby_message_timeout - 1) *
  331. ((int64) 1000);
  332. /* Compute when we need to wakeup to fsync the output file. */
  333. if (fsync_interval > 0 && output_needs_fsync)
  334. fsync_target = output_last_fsync + (fsync_interval - 1) *
  335. ((int64) 1000);
  336. /* Now compute when to wakeup. */
  337. if (message_target > 0 || fsync_target > 0)
  338. {
  339. TimestampTz targettime;
  340. long secs;
  341. int usecs;
  342. targettime = message_target;
  343. if (fsync_target > 0 && fsync_target < targettime)
  344. targettime = fsync_target;
  345. feTimestampDifference(now,
  346. targettime,
  347. &secs,
  348. &usecs);
  349. if (secs <= 0)
  350. timeout.tv_sec = 1; /* Always sleep at least 1 sec */
  351. else
  352. timeout.tv_sec = secs;
  353. timeout.tv_usec = usecs;
  354. timeoutptr = &timeout;
  355. }
  356. r = select(PQsocket(conn) + 1, &input_mask, NULL, NULL, timeoutptr);
  357. if (r == 0 || (r < 0 && errno == EINTR))
  358. {
  359. /*
  360. * Got a timeout or signal. Continue the loop and either
  361. * deliver a status packet to the server or just go back into
  362. * blocking.
  363. */
  364. continue;
  365. }
  366. else if (r < 0)
  367. {
  368. fprintf(stderr, _("%s: select() failed: %s\n"),
  369. progname, strerror(errno));
  370. goto error;
  371. }
  372. /* Else there is actually data on the socket */
  373. if (PQconsumeInput(conn) == 0)
  374. {
  375. fprintf(stderr,
  376. _("%s: could not receive data from WAL stream: %s"),
  377. progname, PQerrorMessage(conn));
  378. goto error;
  379. }
  380. continue;
  381. }
  382. /* End of copy stream */
  383. if (r == -1)
  384. break;
  385. /* Failure while reading the copy stream */
  386. if (r == -2)
  387. {
  388. fprintf(stderr, _("%s: could not read COPY data: %s"),
  389. progname, PQerrorMessage(conn));
  390. goto error;
  391. }
  392. /* Check the message type. */
  393. if (copybuf[0] == 'k')
  394. {
  395. int pos;
  396. bool replyRequested;
  397. XLogRecPtr walEnd;
  398. bool endposReached = false;
  399. /*
  400. * Parse the keepalive message, enclosed in the CopyData message.
  401. * We just check if the server requested a reply, and ignore the
  402. * rest.
  403. */
  404. pos = 1; /* skip msgtype 'k' */
  405. walEnd = fe_recvint64(&copybuf[pos]);
  406. output_written_lsn = Max(walEnd, output_written_lsn);
  407. pos += 8; /* read walEnd */
  408. pos += 8; /* skip sendTime */
  409. if (r < pos + 1)
  410. {
  411. fprintf(stderr, _("%s: streaming header too small: %d\n"),
  412. progname, r);
  413. goto error;
  414. }
  415. replyRequested = copybuf[pos];
  416. if (endpos != InvalidXLogRecPtr && walEnd >= endpos)
  417. {
  418. /*
  419. * If there's nothing to read on the socket until a keepalive
  420. * we know that the server has nothing to send us; and if
  421. * walEnd has passed endpos, we know nothing else can have
  422. * committed before endpos. So we can bail out now.
  423. */
  424. endposReached = true;
  425. }
  426. /* Send a reply, if necessary */
  427. if (replyRequested || endposReached)
  428. {
  429. if (!flushAndSendFeedback(conn, &now))
  430. goto error;
  431. last_status = now;
  432. }
  433. if (endposReached)
  434. {
  435. prepareToTerminate(conn, endpos, true, InvalidXLogRecPtr);
  436. time_to_abort = true;
  437. break;
  438. }
  439. continue;
  440. }
  441. else if (copybuf[0] != 'w')
  442. {
  443. fprintf(stderr, _("%s: unrecognized streaming header: \"%c\"\n"),
  444. progname, copybuf[0]);
  445. goto error;
  446. }
  447. /*
  448. * Read the header of the XLogData message, enclosed in the CopyData
  449. * message. We only need the WAL location field (dataStart), the rest
  450. * of the header is ignored.
  451. */
  452. hdr_len = 1; /* msgtype 'w' */
  453. hdr_len += 8; /* dataStart */
  454. hdr_len += 8; /* walEnd */
  455. hdr_len += 8; /* sendTime */
  456. if (r < hdr_len + 1)
  457. {
  458. fprintf(stderr, _("%s: streaming header too small: %d\n"),
  459. progname, r);
  460. goto error;
  461. }
  462. /* Extract WAL location for this block */
  463. cur_record_lsn = fe_recvint64(&copybuf[1]);
  464. if (endpos != InvalidXLogRecPtr && cur_record_lsn > endpos)
  465. {
  466. /*
  467. * We've read past our endpoint, so prepare to go away being
  468. * cautious about what happens to our output data.
  469. */
  470. if (!flushAndSendFeedback(conn, &now))
  471. goto error;
  472. prepareToTerminate(conn, endpos, false, cur_record_lsn);
  473. time_to_abort = true;
  474. break;
  475. }
  476. output_written_lsn = Max(cur_record_lsn, output_written_lsn);
  477. bytes_left = r - hdr_len;
  478. bytes_written = 0;
  479. /* signal that a fsync is needed */
  480. output_needs_fsync = true;
  481. while (bytes_left)
  482. {
  483. int ret;
  484. ret = write(outfd,
  485. copybuf + hdr_len + bytes_written,
  486. bytes_left);
  487. if (ret < 0)
  488. {
  489. fprintf(stderr,
  490. _("%s: could not write %u bytes to log file \"%s\": %s\n"),
  491. progname, bytes_left, outfile,
  492. strerror(errno));
  493. goto error;
  494. }
  495. /* Write was successful, advance our position */
  496. bytes_written += ret;
  497. bytes_left -= ret;
  498. }
  499. if (write(outfd, "\n", 1) != 1)
  500. {
  501. fprintf(stderr,
  502. _("%s: could not write %u bytes to log file \"%s\": %s\n"),
  503. progname, 1, outfile,
  504. strerror(errno));
  505. goto error;
  506. }
  507. if (endpos != InvalidXLogRecPtr && cur_record_lsn == endpos)
  508. {
  509. /* endpos was exactly the record we just processed, we're done */
  510. if (!flushAndSendFeedback(conn, &now))
  511. goto error;
  512. prepareToTerminate(conn, endpos, false, cur_record_lsn);
  513. time_to_abort = true;
  514. break;
  515. }
  516. }
  517. res = PQgetResult(conn);
  518. if (PQresultStatus(res) == PGRES_COPY_OUT)
  519. {
  520. /*
  521. * We're doing a client-initiated clean exit and have sent CopyDone to
  522. * the server. We've already sent replay confirmation and fsync'd so
  523. * we can just clean up the connection now.
  524. */
  525. goto error;
  526. }
  527. else if (PQresultStatus(res) != PGRES_COMMAND_OK)
  528. {
  529. fprintf(stderr,
  530. _("%s: unexpected termination of replication stream: %s"),
  531. progname, PQresultErrorMessage(res));
  532. goto error;
  533. }
  534. PQclear(res);
  535. if (outfd != -1 && strcmp(outfile, "-") != 0)
  536. {
  537. TimestampTz t = feGetCurrentTimestamp();
  538. /* no need to jump to error on failure here, we're finishing anyway */
  539. OutputFsync(t);
  540. if (close(outfd) != 0)
  541. fprintf(stderr, _("%s: could not close file \"%s\": %s\n"),
  542. progname, outfile, strerror(errno));
  543. }
  544. outfd = -1;
  545. error:
  546. if (copybuf != NULL)
  547. {
  548. PQfreemem(copybuf);
  549. copybuf = NULL;
  550. }
  551. destroyPQExpBuffer(query);
  552. PQfinish(conn);
  553. conn = NULL;
  554. }
  555. /*
  556. * Unfortunately we can't do sensible signal handling on windows...
  557. */
  558. #ifndef WIN32
  559. /*
  560. * When sigint is called, just tell the system to exit at the next possible
  561. * moment.
  562. */
  563. static void
  564. sigint_handler(int signum)
  565. {
  566. time_to_abort = true;
  567. }
  568. /*
  569. * Trigger the output file to be reopened.
  570. */
  571. static void
  572. sighup_handler(int signum)
  573. {
  574. output_reopen = true;
  575. }
  576. #endif
  577. int
  578. main(int argc, char **argv)
  579. {
  580. static struct option long_options[] = {
  581. /* general options */
  582. {"file", required_argument, NULL, 'f'},
  583. {"fsync-interval", required_argument, NULL, 'F'},
  584. {"no-loop", no_argument, NULL, 'n'},
  585. {"verbose", no_argument, NULL, 'v'},
  586. {"version", no_argument, NULL, 'V'},
  587. {"help", no_argument, NULL, '?'},
  588. /* connection options */
  589. {"dbname", required_argument, NULL, 'd'},
  590. {"host", required_argument, NULL, 'h'},
  591. {"port", required_argument, NULL, 'p'},
  592. {"username", required_argument, NULL, 'U'},
  593. {"no-password", no_argument, NULL, 'w'},
  594. {"password", no_argument, NULL, 'W'},
  595. /* replication options */
  596. {"startpos", required_argument, NULL, 'I'},
  597. {"endpos", required_argument, NULL, 'E'},
  598. {"option", required_argument, NULL, 'o'},
  599. {"plugin", required_argument, NULL, 'P'},
  600. {"status-interval", required_argument, NULL, 's'},
  601. {"slot", required_argument, NULL, 'S'},
  602. /* action */
  603. {"create-slot", no_argument, NULL, 1},
  604. {"start", no_argument, NULL, 2},
  605. {"drop-slot", no_argument, NULL, 3},
  606. {"if-not-exists", no_argument, NULL, 4},
  607. {NULL, 0, NULL, 0}
  608. };
  609. int c;
  610. int option_index;
  611. uint32 hi,
  612. lo;
  613. char *db_name;
  614. progname = get_progname(argv[0]);
  615. set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_basebackup"));
  616. if (argc > 1)
  617. {
  618. if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
  619. {
  620. usage();
  621. exit(0);
  622. }
  623. else if (strcmp(argv[1], "-V") == 0 ||
  624. strcmp(argv[1], "--version") == 0)
  625. {
  626. puts("pg_recvlogical (PostgreSQL) " PG_VERSION);
  627. exit(0);
  628. }
  629. }
  630. while ((c = getopt_long(argc, argv, "E:f:F:nvd:h:p:U:wWI:o:P:s:S:",
  631. long_options, &option_index)) != -1)
  632. {
  633. switch (c)
  634. {
  635. /* general options */
  636. case 'f':
  637. outfile = pg_strdup(optarg);
  638. break;
  639. case 'F':
  640. fsync_interval = atoi(optarg) * 1000;
  641. if (fsync_interval < 0)
  642. {
  643. fprintf(stderr, _("%s: invalid fsync interval \"%s\"\n"),
  644. progname, optarg);
  645. exit(1);
  646. }
  647. break;
  648. case 'n':
  649. noloop = 1;
  650. break;
  651. case 'v':
  652. verbose++;
  653. break;
  654. /* connection options */
  655. case 'd':
  656. dbname = pg_strdup(optarg);
  657. break;
  658. case 'h':
  659. dbhost = pg_strdup(optarg);
  660. break;
  661. case 'p':
  662. if (atoi(optarg) <= 0)
  663. {
  664. fprintf(stderr, _("%s: invalid port number \"%s\"\n"),
  665. progname, optarg);
  666. exit(1);
  667. }
  668. dbport = pg_strdup(optarg);
  669. break;
  670. case 'U':
  671. dbuser = pg_strdup(optarg);
  672. break;
  673. case 'w':
  674. dbgetpassword = -1;
  675. break;
  676. case 'W':
  677. dbgetpassword = 1;
  678. break;
  679. /* replication options */
  680. case 'I':
  681. if (sscanf(optarg, "%X/%X", &hi, &lo) != 2)
  682. {
  683. fprintf(stderr,
  684. _("%s: could not parse start position \"%s\"\n"),
  685. progname, optarg);
  686. exit(1);
  687. }
  688. startpos = ((uint64) hi) << 32 | lo;
  689. break;
  690. case 'E':
  691. if (sscanf(optarg, "%X/%X", &hi, &lo) != 2)
  692. {
  693. fprintf(stderr,
  694. _("%s: could not parse end position \"%s\"\n"),
  695. progname, optarg);
  696. exit(1);
  697. }
  698. endpos = ((uint64) hi) << 32 | lo;
  699. break;
  700. case 'o':
  701. {
  702. char *data = pg_strdup(optarg);
  703. char *val = strchr(data, '=');
  704. if (val != NULL)
  705. {
  706. /* remove =; separate data from val */
  707. *val = '\0';
  708. val++;
  709. }
  710. noptions += 1;
  711. options = pg_realloc(options, sizeof(char *) * noptions * 2);
  712. options[(noptions - 1) * 2] = data;
  713. options[(noptions - 1) * 2 + 1] = val;
  714. }
  715. break;
  716. case 'P':
  717. plugin = pg_strdup(optarg);
  718. break;
  719. case 's':
  720. standby_message_timeout = atoi(optarg) * 1000;
  721. if (standby_message_timeout < 0)
  722. {
  723. fprintf(stderr, _("%s: invalid status interval \"%s\"\n"),
  724. progname, optarg);
  725. exit(1);
  726. }
  727. break;
  728. case 'S':
  729. replication_slot = pg_strdup(optarg);
  730. break;
  731. /* action */
  732. case 1:
  733. do_create_slot = true;
  734. break;
  735. case 2:
  736. do_start_slot = true;
  737. break;
  738. case 3:
  739. do_drop_slot = true;
  740. break;
  741. case 4:
  742. slot_exists_ok = true;
  743. break;
  744. default:
  745. /*
  746. * getopt_long already emitted a complaint
  747. */
  748. fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
  749. progname);
  750. exit(1);
  751. }
  752. }
  753. /*
  754. * Any non-option arguments?
  755. */
  756. if (optind < argc)
  757. {
  758. fprintf(stderr,
  759. _("%s: too many command-line arguments (first is \"%s\")\n"),
  760. progname, argv[optind]);
  761. fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
  762. progname);
  763. exit(1);
  764. }
  765. /*
  766. * Required arguments
  767. */
  768. if (replication_slot == NULL)
  769. {
  770. fprintf(stderr, _("%s: no slot specified\n"), progname);
  771. fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
  772. progname);
  773. exit(1);
  774. }
  775. if (do_start_slot && outfile == NULL)
  776. {
  777. fprintf(stderr, _("%s: no target file specified\n"), progname);
  778. fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
  779. progname);
  780. exit(1);
  781. }
  782. if (!do_drop_slot && dbname == NULL)
  783. {
  784. fprintf(stderr, _("%s: no database specified\n"), progname);
  785. fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
  786. progname);
  787. exit(1);
  788. }
  789. if (!do_drop_slot && !do_create_slot && !do_start_slot)
  790. {
  791. fprintf(stderr, _("%s: at least one action needs to be specified\n"), progname);
  792. fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
  793. progname);
  794. exit(1);
  795. }
  796. if (do_drop_slot && (do_create_slot || do_start_slot))
  797. {
  798. fprintf(stderr, _("%s: cannot use --create-slot or --start together with --drop-slot\n"), progname);
  799. fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
  800. progname);
  801. exit(1);
  802. }
  803. if (startpos != InvalidXLogRecPtr && (do_create_slot || do_drop_slot))
  804. {
  805. fprintf(stderr, _("%s: cannot use --create-slot or --drop-slot together with --startpos\n"), progname);
  806. fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
  807. progname);
  808. exit(1);
  809. }
  810. if (endpos != InvalidXLogRecPtr && !do_start_slot)
  811. {
  812. fprintf(stderr,
  813. _("%s: --endpos may only be specified with --start\n"),
  814. progname);
  815. fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
  816. progname);
  817. exit(1);
  818. }
  819. #ifndef WIN32
  820. pqsignal(SIGINT, sigint_handler);
  821. pqsignal(SIGHUP, sighup_handler);
  822. #endif
  823. /*
  824. * Obtain a connection to server. This is not really necessary but it
  825. * helps to get more precise error messages about authentication, required
  826. * GUC parameters and such.
  827. */
  828. conn = GetConnection();
  829. if (!conn)
  830. /* Error message already written in GetConnection() */
  831. exit(1);
  832. /*
  833. * Run IDENTIFY_SYSTEM to make sure we connected using a database specific
  834. * replication connection.
  835. */
  836. if (!RunIdentifySystem(conn, NULL, NULL, NULL, &db_name))
  837. disconnect_and_exit(1);
  838. if (db_name == NULL)
  839. {
  840. fprintf(stderr,
  841. _("%s: could not establish database-specific replication connection\n"),
  842. progname);
  843. disconnect_and_exit(1);
  844. }
  845. /*
  846. * Set umask so that directories/files are created with the same
  847. * permissions as directories/files in the source data directory.
  848. *
  849. * pg_mode_mask is set to owner-only by default and then updated in
  850. * GetConnection() where we get the mode from the server-side with
  851. * RetrieveDataDirCreatePerm() and then call SetDataDirectoryCreatePerm().
  852. */
  853. umask(pg_mode_mask);
  854. /* Drop a replication slot. */
  855. if (do_drop_slot)
  856. {
  857. if (verbose)
  858. fprintf(stderr,
  859. _("%s: dropping replication slot \"%s\"\n"),
  860. progname, replication_slot);
  861. if (!DropReplicationSlot(conn, replication_slot))
  862. disconnect_and_exit(1);
  863. }
  864. /* Create a replication slot. */
  865. if (do_create_slot)
  866. {
  867. if (verbose)
  868. fprintf(stderr,
  869. _("%s: creating replication slot \"%s\"\n"),
  870. progname, replication_slot);
  871. if (!CreateReplicationSlot(conn, replication_slot, plugin, false,
  872. false, false, slot_exists_ok))
  873. disconnect_and_exit(1);
  874. startpos = InvalidXLogRecPtr;
  875. }
  876. if (!do_start_slot)
  877. disconnect_and_exit(0);
  878. /* Stream loop */
  879. while (true)
  880. {
  881. StreamLogicalLog();
  882. if (time_to_abort)
  883. {
  884. /*
  885. * We've been Ctrl-C'ed or reached an exit limit condition. That's
  886. * not an error, so exit without an errorcode.
  887. */
  888. disconnect_and_exit(0);
  889. }
  890. else if (noloop)
  891. {
  892. fprintf(stderr, _("%s: disconnected\n"), progname);
  893. exit(1);
  894. }
  895. else
  896. {
  897. fprintf(stderr,
  898. /* translator: check source for value for %d */
  899. _("%s: disconnected; waiting %d seconds to try again\n"),
  900. progname, RECONNECT_SLEEP_TIME);
  901. pg_usleep(RECONNECT_SLEEP_TIME * 1000000);
  902. }
  903. }
  904. }
  905. /*
  906. * Fsync our output data, and send a feedback message to the server. Returns
  907. * true if successful, false otherwise.
  908. *
  909. * If successful, *now is updated to the current timestamp just before sending
  910. * feedback.
  911. */
  912. static bool
  913. flushAndSendFeedback(PGconn *conn, TimestampTz *now)
  914. {
  915. /* flush data to disk, so that we send a recent flush pointer */
  916. if (!OutputFsync(*now))
  917. return false;
  918. *now = feGetCurrentTimestamp();
  919. if (!sendFeedback(conn, *now, true, false))
  920. return false;
  921. return true;
  922. }
  923. /*
  924. * Try to inform the server about our upcoming demise, but don't wait around or
  925. * retry on failure.
  926. */
  927. static void
  928. prepareToTerminate(PGconn *conn, XLogRecPtr endpos, bool keepalive, XLogRecPtr lsn)
  929. {
  930. (void) PQputCopyEnd(conn, NULL);
  931. (void) PQflush(conn);
  932. if (verbose)
  933. {
  934. if (keepalive)
  935. fprintf(stderr, "%s: endpos %X/%X reached by keepalive\n",
  936. progname,
  937. (uint32) (endpos >> 32), (uint32) endpos);
  938. else
  939. fprintf(stderr, "%s: endpos %X/%X reached by record at %X/%X\n",
  940. progname, (uint32) (endpos >> 32), (uint32) (endpos),
  941. (uint32) (lsn >> 32), (uint32) lsn);
  942. }
  943. }