1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ///////////////////////////////////////////////////////////////////////////
16 
17 /*-------------------------------------------------------------------------
18  *
19  *
20  * This code is released under the terms of the PostgreSQL License.
21  *
22  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
23  * Portions Copyright (c) 1994, Regents of the University of California
24  *
25  *-------------------------------------------------------------------------
26  */
27 #include "postgres_fe.h"
28 
29 #include <sys/stat.h>
30 #include <sys/wait.h>
31 
32 #include "common/logging.h"
33 #include "common/restricted_token.h"
34 #include "libpq/pqcomm.h"
35 #include "pg_config_paths.h"
36 #include "pg_regress.h"
37 
38 const char *progname = "progname";
39 static char *shellprog = SHELLPROG;
40 char *outputdir = ".";
41 static char *temp_instance = NULL;
42 static int port = -1;
43 static const char *sockdir;
44 static PID_TYPE postmaster_pid = INVALID_PID;
45 
psql_command(const char * database,const char * query,...)46 static void psql_command(const char *database, const char *query, ...) {
47   char query_formatted[1024];
48   char query_escaped[2048];
49   char psql_cmd[MAXPGPATH + 2048];
50   va_list args;
51   char *s;
52   char *d;
53 
54   va_start(args, query);
55   vsnprintf(query_formatted, sizeof(query_formatted), query, args);
56   va_end(args);
57 
58   d = query_escaped;
59   for (s = query_formatted; *s; s++) {
60     if (strchr("\\\"$`", *s))
61       *d++ = '\\';
62     *d++ = *s;
63   }
64   *d = '\0';
65 
66   snprintf(psql_cmd, sizeof(psql_cmd), "\"%s%spsql\" -X -c \"%s\" \"%s\"",
67            "", "", query_escaped, database);
68 
69   system(psql_cmd);
70 }
71 
72 PID_TYPE
spawn_process(const char * cmdline)73 spawn_process(const char *cmdline) {
74   pid_t pid;
75   pid = fork();
76   if (pid == 0) {
77     char *cmdline2;
78     cmdline2 = psprintf("exec %s", cmdline);
79     execl(shellprog, shellprog, "-c", cmdline2, (char *)NULL);
80     fprintf(stderr, _("%s: could not exec \"%s\": %s\n"), progname, shellprog,
81             strerror(errno));
82     _exit(1);
83   }
84 
85   return pid;
86 }
87 
main()88 int main() {
89   int i;
90   char buf[MAXPGPATH * 4];
91   char buf2[MAXPGPATH * 4];
92   char *db_name = "./dbfuzz";
93   int wait_seconds = 60;
94 
95   pg_logging_init(db_name);
96   progname = get_progname(db_name);
97   set_pglocale_pgservice(db_name, PG_TEXTDOMAIN("pg_dbfuzz"));
98   get_restricted_token();
99 
100   temp_instance = make_absolute_path("./temp");
101   port = 0xC000 | (PG_VERSION_NUM & 0x3FFF);
102   outputdir = make_absolute_path(outputdir);
103   sockdir = mkdtemp(psprintf("/tmp/pg_dbfuzz-XXXXXX"));
104   putenv(psprintf("PGHOST=%s", sockdir));
105 
106   mkdir(temp_instance, S_IRWXU | S_IRWXG | S_IRWXO);
107 
108   snprintf(buf, sizeof(buf), "%s/log", outputdir);
109   mkdir(buf, S_IRWXU | S_IRWXG | S_IRWXO);
110 
111   snprintf(buf, sizeof(buf),
112            "\"%s%sinitdb\" -D \"%s/data\" --no-clean --no-sync > "
113            "\"%s/log/initdb.log\" 2>&1",
114            "", "", temp_instance, outputdir);
115   system(buf);
116 
117   snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_instance);
118 
119   snprintf(buf2, sizeof(buf2), "\"%s%spsql\" -X postgres <%s 2>%s", "", "",
120            DEVNULL, DEVNULL);
121 
122   snprintf(buf, sizeof(buf),
123            "\"%s%spostgres\" -D \"%s/data\" -F%s "
124            "-c \"listen_addresses=%s\" -k \"%s\" "
125            "> \"%s/log/postmaster.log\" 2>&1",
126            "", "", temp_instance, "", "", sockdir, outputdir);
127 
128   postmaster_pid = spawn_process(buf);
129 
130   for (i = 0; i < wait_seconds; i++) {
131     if (system(buf2) == 0)
132       break;
133     waitpid(postmaster_pid, NULL, WNOHANG);
134     pg_usleep(1000000L);
135   }
136 
137   psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0%s",
138                "dbfuzz", "");
139 
140   snprintf(buf, sizeof(buf), "\"%s%spg_ctl\" stop -D \"%s/data\" -s", "",
141            "", temp_instance);
142   system(buf);
143 
144   rmdir(sockdir);
145   return 0;
146 }
147