1 /*
2 * Copyright (c) 2017-2018 Tatsuo Ishii
3 * Copyright (c) 2018-2021 PgPool Global Development Group
4 *
5 * Permission to use, copy, modify, and distribute this software and
6 * its documentation for any purpose and without fee is hereby
7 * granted, provided that the above copyright notice appear in all
8 * copies and that both that copyright notice and this permission
9 * notice appear in supporting documentation, and that the name of the
10 * author not be used in advertising or publicity pertaining to
11 * distribution of the software without specific, written prior
12 * permission. The author makes no representations about the
13 * suitability of this software for any purpose. It is provided "as
14 * is" without express or implied warranty.
15 */
16
17 #include "../../include/config.h"
18 #include "pgproto/pgproto.h"
19 #include <unistd.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <getopt.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include "pgproto/fe_memutils.h"
27 #include <libpq-fe.h>
28 #include "pgproto/read.h"
29 #include "pgproto/send.h"
30 #include "pgproto/buffer.h"
31 #include "pgproto/extended_query.h"
32
33 #undef DEBUG
34
35 static void show_version(void);
36 static void usage(void);
37 static FILE *openfile(char *filename);
38 static PGconn *connect_db(char *host, char *port, char *user, char *database);
39 static void read_and_process(FILE *fd, PGconn *conn);
40 static int process_a_line(char *buf, PGconn *con);
41 static int process_message_type(int kind, char *buf, PGconn *conn);
42 static void process_function_call(char *buf, PGconn *conn);
43
44 int read_nap = 0;
45
46 int
main(int argc,char ** argv)47 main(int argc, char **argv)
48 {
49 int opt;
50 int optindex;
51 char *env;
52 char *host = "";
53 char *port = "5432";
54 char *user = "";
55 char *database = "";
56 char *data_file = PGPROTODATA;
57 int debug = 0;
58 FILE *fd;
59 PGconn *con;
60 int var;
61
62 static struct option long_options[] = {
63 {"host", optional_argument, NULL, 'h'},
64 {"port", optional_argument, NULL, 'p'},
65 {"username", optional_argument, NULL, 'u'},
66 {"database", optional_argument, NULL, 'd'},
67 {"proto-data-file", optional_argument, NULL, 'f'},
68 {"debug", no_argument, NULL, 'D'},
69 {"help", no_argument, NULL, '?'},
70 {"version", no_argument, NULL, 'v'},
71 {"read-nap", optional_argument, NULL, 'r'},
72 {NULL, 0, NULL, 0}
73 };
74
75 if ((env = getenv("PGHOST")) != NULL && *env != '\0')
76 host = env;
77 if ((env = getenv("PGPORT")) != NULL && *env != '\0')
78 port = env;
79 if ((env = getenv("PGDATABASE")) != NULL && *env != '\0')
80 database = env;
81 if ((env = getenv("PGUSER")) != NULL && *env != '\0')
82 user = env;
83
84 while ((opt = getopt_long(argc, argv, "v?Dh:p:u:d:f:r:", long_options, &optindex)) != -1)
85 {
86 switch (opt)
87 {
88 case 'v':
89 show_version();
90 exit(0);
91 break;
92
93 case '?':
94 usage();
95 exit(0);
96 break;
97
98 case 'D':
99 debug++;
100 break;
101
102 case 'h':
103 host = pg_strdup(optarg);
104 break;
105
106 case 'p':
107 port = pg_strdup(optarg);
108 break;
109
110 case 'u':
111 user = pg_strdup(optarg);
112 break;
113
114 case 'd':
115 database = pg_strdup(optarg);
116 break;
117
118 case 'f':
119 data_file = pg_strdup(optarg);
120 break;
121
122 case 'r':
123 read_nap = atoi(optarg);
124 break;
125
126 default:
127 usage();
128 exit(1);
129 }
130 }
131
132 fd = openfile(data_file);
133 con = connect_db(host, port, user, database);
134 var = fcntl(PQsocket(con), F_GETFL, 0);
135 if (var == -1)
136 {
137 fprintf(stderr, "fcntl failed (%s)\n", strerror(errno));
138 exit(1);
139 }
140
141 /*
142 * Set the socket to non block.
143 */
144 if (fcntl(PQsocket(con), F_SETFL, var & ~O_NONBLOCK) == -1)
145 {
146 fprintf(stderr, "fcntl failed (%s)\n", strerror(errno));
147 exit(1);
148 }
149
150 read_and_process(fd, con);
151
152 return 0;
153 }
154
155 static void
show_version(void)156 show_version(void)
157 {
158 fprintf(stderr, "pgproto (%s) %s\n", PACKAGE, PACKAGE_VERSION);
159 }
160
161 static void
usage(void)162 usage(void)
163 {
164 printf("Usage: %s\n"
165 "-h, --hostname=HOSTNAME (default: UNIX domain socket)\n"
166 "-p, --port=PORT (default: 5432)\n"
167 "-u, --user USERNAME (default: OS user)\n"
168 "-d, --database DATABASENAME (default: same as user)\n"
169 "-f, --proto-data-file FILENAME (default: pgproto.data)\n"
170 "-r, --read-nap NAPTIME (in micro seconds. default: 0)\n"
171 "-D, --debug\n"
172 "-?, --help\n"
173 "-v, --version\n",
174 PACKAGE);
175 }
176
177 /*
178 * Open protocol data and return the file descriptor. If failed to open the
179 * file, do not return and exit within this function.
180 */
181 static FILE *
openfile(char * filename)182 openfile(char *filename)
183 {
184 FILE *fd = fopen(filename, "r");
185
186 if (fd == NULL)
187 {
188 fprintf(stderr, "Failed to open protocol data file: %s.\n", filename);
189 exit(1);
190 }
191 return fd;
192 }
193
194 /*
195 * Connect to the specified PostgreSQL. If failed, do not return and exit
196 * within this function.
197 */
198 static PGconn *
connect_db(char * host,char * port,char * user,char * database)199 connect_db(char *host, char *port, char *user, char *database)
200 {
201 char conninfo[1024];
202 PGconn *conn;
203 size_t n;
204 char *app_name_str = " application_name=pgproto";
205
206 conninfo[0] = '\0';
207 n = sizeof(conninfo);
208
209 if (host && host[0] != '\0')
210 {
211 n -= sizeof("host=");
212 strncat(conninfo, "host=", n);
213 n -= strlen(host) + 1;
214 strncat(conninfo, host, n);
215 }
216
217 if (port && port[0] != '\0')
218 {
219 n -= sizeof("port=");
220 strncat(conninfo, " port=", n);
221 n -= strlen(port) + 1;
222 strncat(conninfo, port, n);
223 }
224
225 if (user && user[0] != '\0')
226 {
227 n -= sizeof("user=");
228 strncat(conninfo, " user=", n);
229 n -= strlen(user) + 1;
230 strncat(conninfo, user, n);
231 }
232
233 if (database && database[0] != '\0')
234 {
235 n -= sizeof("dbname=");
236 strncat(conninfo, " dbname=", n);
237 n -= strlen(database) + 1;
238 strncat(conninfo, database, n);
239 }
240
241 n -= strlen(app_name_str);
242 strncat(conninfo, app_name_str, n);
243
244 conn = PQconnectdb(conninfo);
245
246 if (conn == NULL || PQstatus(conn) == CONNECTION_BAD)
247 {
248 fprintf(stderr, "Failed to connect to %s.\n", conninfo);
249 exit(1);
250 }
251
252 return conn;
253 }
254
255 /*
256 * Read the protocol data file and process it.
257 */
258 static void
read_and_process(FILE * fd,PGconn * conn)259 read_and_process(FILE *fd, PGconn *conn)
260 {
261 #define PGPROTO_READBUF_LENGTH 8192
262 int status;
263 char *buf;
264 int buflen;
265 char *p;
266 int len;
267 int readp;
268
269 for (;;)
270 {
271 buflen = PGPROTO_READBUF_LENGTH;
272 buf = pg_malloc(buflen);
273 readp = 0;
274
275 for (;;)
276 {
277 p = fgets(buf + readp, buflen - readp, fd);
278 if (p == NULL)
279 {
280 /* EOF detected */
281 exit(0);
282 }
283
284 /*
285 * if ends with backslash + new line, assume it's a continuous
286 * line
287 */
288 len = strlen(p);
289 if (p[len - 2] != '\\' || p[len - 1] != '\n')
290 {
291 break;
292 }
293
294 buflen += PGPROTO_READBUF_LENGTH;
295 buf = pg_realloc(buf, buflen);
296 readp += len;
297 }
298
299 status = process_a_line(buf, conn);
300 pg_free(buf);
301
302 if (status < 0)
303 {
304 exit(1);
305 }
306 }
307 PQfinish(conn);
308 }
309
310 /*
311 * Process a line of protocol data.
312 */
313 static int
process_a_line(char * buf,PGconn * conn)314 process_a_line(char *buf, PGconn *conn)
315 {
316 char *p = buf;
317 int kind;
318
319 #ifdef DEBUG
320 fprintf(stderr, "buf: %s", buf);
321 #endif
322
323 if (p == NULL)
324 {
325 fprintf(stderr, "process_a_line: buf is NULL\n");
326 return -1;
327 }
328
329 /* An empty line or a line starting with '#' is ignored. */
330 if (*p == '\n' || *p == '#')
331 {
332 return 0;
333 }
334 else if (*p != '\'')
335 {
336 fprintf(stderr, "process_a_line: first character is not \' but %x\n", *p);
337 return -1;
338 }
339
340 p++;
341
342 kind = (unsigned char) (*p++);
343
344 if (*p != '\'')
345 {
346 fprintf(stderr, "process_a_line: message kind is not followed by \' but %x\n", *p);
347 return -1;
348 }
349
350 p++;
351
352 process_message_type(kind, p, conn);
353
354 return 0;
355 }
356
357 /*
358 * Read a line of message from buf and process it.
359 */
360 static int
process_message_type(int kind,char * buf,PGconn * conn)361 process_message_type(int kind, char *buf, PGconn *conn)
362 {
363 #ifdef DEBUG
364 fprintf(stderr, "message kind is %c\n", kind);
365 #endif
366
367 char *query;
368 char *err_msg;
369 char *data;
370 char *bufp;
371
372 switch (kind)
373 {
374 case 'Y':
375 read_until_ready_for_query(conn, 0);
376 break;
377
378 case 'y':
379 read_until_ready_for_query(conn, 1);
380 break;
381
382 case 'X':
383 fprintf(stderr, "FE=> Terminate\n");
384 send_char((char) kind, conn);
385 send_int(sizeof(int), conn);
386 break;
387
388 case 'S':
389 fprintf(stderr, "FE=> Sync\n");
390 send_char((char) kind, conn);
391 send_int(sizeof(int), conn);
392 break;
393
394 case 'H':
395 fprintf(stderr, "FE=> Flush\n");
396 send_char((char) kind, conn);
397 send_int(sizeof(int), conn);
398 break;
399
400 case 'Q':
401 buf++;
402 query = buffer_read_string(buf, &bufp);
403 fprintf(stderr, "FE=> Query (query=\"%s\")\n", query);
404 send_char((char) kind, conn);
405 send_int(sizeof(int) + strlen(query) + 1, conn);
406 send_string(query, conn);
407 pg_free(query);
408 break;
409
410 case 'd':
411 buf++;
412 data = buffer_read_string(buf, &bufp);
413 fprintf(stderr, "FE=> CopyData (copy data=\"%s\")\n", data);
414 send_char((char) kind, conn);
415 send_int(sizeof(int) + strlen(data), conn);
416 send_byte(data, strlen(data), conn);
417 pg_free(data);
418 break;
419
420 case 'c':
421 fprintf(stderr, "FE=> CopyDone\n");
422 send_char((char) kind, conn);
423 send_int(sizeof(int), conn);
424 break;
425
426 case 'f':
427 buf++;
428 err_msg = buffer_read_string(buf, &bufp);
429 fprintf(stderr, "FE=> CopyFail (error message=\"%s\")\n", err_msg);
430 send_char((char) kind, conn);
431 send_int(sizeof(int) + strlen(err_msg) + 1, conn);
432 send_string(err_msg, conn);
433 pg_free(err_msg);
434 break;
435
436 case 'P':
437 process_parse(buf, conn);
438 break;
439
440 case 'B':
441 process_bind(buf, conn);
442 break;
443
444 case 'E':
445 process_execute(buf, conn);
446 break;
447
448 case 'D':
449 process_describe(buf, conn);
450 break;
451
452 case 'C':
453 process_close(buf, conn);
454 break;
455
456 case 'F':
457 process_function_call(buf, conn);
458 break;
459
460 default:
461 fprintf(stderr, "Unknown kind: %c", kind);
462 break;
463 }
464 return 0;
465 }
466
467 /*
468 * Process function call message
469 */
470 static void
process_function_call(char * buf,PGconn * conn)471 process_function_call(char *buf, PGconn *conn)
472 {
473 int len;
474 int foid;
475 short nparams;
476 short ncodes;
477 short codes[MAXENTRIES];
478 int paramlens[MAXENTRIES];
479 char *paramvals[MAXENTRIES];
480 short result_formatcode;
481 int i;
482 char *bufp;
483
484 SKIP_TABS(buf);
485
486 len = sizeof(int);
487
488 /* function oid */
489 foid = buffer_read_int(buf, &bufp);
490 buf = bufp;
491 len += sizeof(int);
492
493 SKIP_TABS(buf);
494
495 /* number of argument format codes */
496 ncodes = buffer_read_int(buf, &bufp);
497 len += sizeof(short) + sizeof(short) * ncodes;
498 buf = bufp;
499
500 SKIP_TABS(buf);
501
502 if (ncodes > MAXENTRIES)
503 {
504 fprintf(stderr, "Too many argument format codes for function call message (%d)\n", ncodes);
505 exit(1);
506 }
507
508 /* read each format code */
509 if (ncodes > 0)
510 {
511 for (i = 0; i < ncodes; i++)
512 {
513 codes[i] = buffer_read_int(buf, &bufp);
514 buf = bufp;
515 SKIP_TABS(buf);
516 }
517 }
518
519 /* number of function arguments */
520 nparams = buffer_read_int(buf, &bufp);
521 len += sizeof(short);
522 buf = bufp;
523 SKIP_TABS(buf);
524
525 if (nparams > MAXENTRIES)
526 {
527 fprintf(stderr, "Too many function arguments for function call message (%d)\n", nparams);
528 exit(1);
529 }
530
531 /* read each function argument */
532 for (i = 0; i < nparams; i++)
533 {
534 paramlens[i] = buffer_read_int(buf, &bufp);
535 len += sizeof(int);
536 buf = bufp;
537 SKIP_TABS(buf);
538
539 if (paramlens[i] > 0)
540 {
541 paramvals[i] = buffer_read_string(buf, &bufp);
542 buf = bufp;
543 SKIP_TABS(buf);
544 len += paramlens[i];
545 }
546 }
547
548 SKIP_TABS(buf);
549
550 /* result format code */
551 result_formatcode = buffer_read_int(buf, &bufp);
552
553 if (result_formatcode != 0 && result_formatcode != 1)
554 {
555 fprintf(stderr, "Result format code is not either 0 or 1 (%d)\n", result_formatcode);
556 exit(1);
557 }
558
559 buf = bufp;
560 len += sizeof(short);
561 SKIP_TABS(buf);
562
563 fprintf(stderr, "\n");
564
565 send_char('F', conn);
566 send_int(len, conn); /* message length */
567 send_int(foid, conn); /* function oid */
568 send_int16(ncodes, conn); /* number of argument format code */
569 for (i = 0; i < ncodes; i++) /* argument format codes */
570 {
571 send_int16(codes[i], conn);
572 }
573
574 send_int16(nparams, conn); /* number of function arguments */
575 for (i = 0; i < nparams; i++) /* function arguments */
576 {
577 send_int(paramlens[i], conn); /* argument length */
578
579 /* NULL? */
580 if (paramlens[i] != -1)
581 {
582 /*
583 * Actually we only support text format only for now. To support
584 * binary format, we need a binary expression in the data file.
585 */
586 send_byte(paramvals[i], paramlens[i], conn);
587 #ifdef NOT_USED
588 if (ncodes == 0) /* text format? */
589 {
590 send_byte(paramvals[i], paramlens[i], conn);
591 }
592 else if (ncodes == 1)
593 {
594 if (codes[0] == 0)
595 send_byte(paramvals[i], paramlens[i], conn);
596 else
597 send_byte(paramvals[i], paramlens[i], conn);
598 }
599 else
600 {
601 if (codes[i] == 0)
602 send_byte(paramvals[i], paramlens[i], conn);
603 else
604 send_byte(paramvals[i], paramlens[i], conn);
605 }
606 #endif
607 }
608 }
609
610 /* result format code */
611 send_int16(result_formatcode, conn);
612 }
613