1 #include "common.h"
2 
3 #if HAVE_UNISTD_H
4 #include <unistd.h>
5 #endif
6 
7 #if HAVE_LIBGEN_H
8 #include <libgen.h>
9 #endif /* HAVE_LIBGEN_H */
10 
11 #if HAVE_SYS_PARAM_H
12 #include <sys/param.h>
13 #endif /* HAVE_SYS_PARAM_H */
14 
15 #include <freetds/time.h>
16 
17 #if HAVE_SYS_RESOURCE_H
18 #include <sys/resource.h>
19 #endif /* HAVE_SYS_RESOURCE_H */
20 
21 #include "replacements.h"
22 
23 #include <common/test_assert.h>
24 
25 #if !defined(PATH_MAX)
26 #define PATH_MAX 256
27 #endif
28 
29 char USER[512];
30 char SERVER[512];
31 char PASSWORD[512];
32 char DATABASE[512];
33 
34 static char sql_file[PATH_MAX];
35 static FILE* input_file = NULL;
36 
37 static char *ARGV0 = NULL;
38 static char *ARGV0B = NULL;
39 static char *DIRNAME = NULL;
40 static const char *BASENAME = NULL;
41 
42 #if HAVE_MALLOC_OPTIONS
43 extern const char *malloc_options;
44 #endif /* HAVE_MALLOC_OPTIONS */
45 
46 void
set_malloc_options(void)47 set_malloc_options(void)
48 {
49 
50 #if HAVE_MALLOC_OPTIONS
51 	/*
52 	 * Options for malloc
53 	 * A- all warnings are fatal
54 	 * J- init memory to 0xD0
55 	 * R- always move memory block on a realloc
56 	 */
57 	malloc_options = "AJR";
58 #endif /* HAVE_MALLOC_OPTIONS */
59 }
60 
61 #if defined(__MINGW32__) || defined(_MSC_VER)
62 static char *
tds_dirname(char * path)63 tds_dirname(char* path)
64 {
65 	char *p, *p2;
66 
67 	for (p = path + strlen(path); --p > path && (*p == '/' || *p == '\\');)
68 		*p = '\0';
69 
70 	p = strrchr(path, '/');
71 	if (!p)
72 		p = path;
73 	p2 = strrchr(p, '\\');
74 	if (p2)
75 		p = p2;
76 	if (p == path) {
77 		if (*p == '/' || *p == '\\')
78 			return "\\";
79 		return ".";
80 	}
81 	*p = 0;
82 	return path;
83 }
84 #define dirname tds_dirname
85 
86 #endif
87 
88 char free_file_registered = 0;
89 static void
free_file(void)90 free_file(void)
91 {
92 	if (input_file) {
93 		fclose(input_file);
94 		input_file = NULL;
95 	}
96 	if (ARGV0) {
97 		DIRNAME = NULL;
98 		BASENAME = NULL;
99 		free(ARGV0);
100 		ARGV0 = NULL;
101 		free(ARGV0B);
102 		ARGV0B = NULL;
103 	}
104 }
105 
106 int
read_login_info(int argc,char ** argv)107 read_login_info(int argc, char **argv)
108 {
109 	int len;
110 	FILE *in = NULL;
111 #if !defined(__MINGW32__) && !defined(_MSC_VER)
112 	int ch;
113 #endif
114 	char line[512];
115 	char *s1, *s2;
116 	char filename[PATH_MAX];
117 	static const char *PWD = "../../../PWD";
118 	struct { char *username, *password, *servername, *database; char fverbose; } options;
119 
120 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_STACK)
121 #define MAX_STACK (8*1024*1024)
122 
123 	struct rlimit rlim;
124 
125 	if (!getrlimit(RLIMIT_STACK, &rlim) && (rlim.rlim_cur == RLIM_INFINITY || rlim.rlim_cur > MAX_STACK)) {
126 		rlim.rlim_cur = MAX_STACK;
127 		setrlimit(RLIMIT_STACK, &rlim);
128 	}
129 #endif
130 
131 	setbuf(stdout, NULL);
132 	setbuf(stderr, NULL);
133 
134 	free(ARGV0);
135 	free(ARGV0B);
136 #ifdef __VMS
137 	{
138 		/* basename expects unix format */
139 		s1 = strrchr(argv[0], ';'); /* trim version; extension trimmed later */
140 		if (s1) *s1 = 0;
141 		const char *unixspec = decc$translate_vms(argv[0]);
142 		ARGV0 = strdup(unixspec);
143 	}
144 #else
145 	ARGV0 = strdup(argv[0]);
146 #endif
147         ARGV0B = strdup(ARGV0);
148 
149 	BASENAME = basename(ARGV0B);
150 #if defined(_WIN32) || defined(__VMS)
151 	s1 = strrchr(BASENAME, '.');
152 	if (s1) *s1 = 0;
153 #endif
154 	DIRNAME = dirname(ARGV0);
155 
156 	memset(&options, 0, sizeof(options));
157 
158 #if !defined(__MINGW32__) && !defined(_MSC_VER)
159 	/* process command line options (handy for manual testing) */
160 	while ((ch = getopt(argc, (char**)argv, "U:P:S:D:f:v")) != -1) {
161 		switch (ch) {
162 		case 'U':
163 			options.username = strdup(optarg);
164 			break;
165 		case 'P':
166 			options.password = strdup(optarg);
167 			break;
168 		case 'S':
169 			options.servername = strdup(optarg);
170 			break;
171 		case 'D':
172 			options.database = strdup(optarg);
173 			break;
174 		case 'f': /* override default PWD file */
175 			PWD = strdup(optarg);
176 			break;
177 		case 'v':
178 			options.fverbose = 1; /* doesn't normally do anything */
179 			break;
180 		case '?':
181 		default:
182 			fprintf(stderr, "usage:  %s \n"
183 					"        [-U username] [-P password]\n"
184 					"        [-S servername] [-D database]\n"
185 					"        [-i input filename] [-o output filename] "
186 					"[-e error filename]\n"
187 					, BASENAME);
188 			exit(1);
189 		}
190 	}
191 #endif
192 	strcpy(filename, PWD);
193 
194 	s1 = getenv("TDSPWDFILE");
195 	if (s1 && s1[0])
196 		in = fopen(s1, "r");
197 	if (!in)
198 		in = fopen(filename, "r");
199 	if (!in)
200 		in = fopen("PWD", "r");
201 	if (!in) {
202 		sprintf(filename, "%s/%s", (DIRNAME) ? DIRNAME : ".", PWD);
203 
204 		in = fopen(filename, "r");
205 		if (!in) {
206 			fprintf(stderr, "Can not open %s file\n\n", filename);
207 			goto Override;
208 		}
209 	}
210 
211 	while (fgets(line, 512, in)) {
212 		s1 = strtok(line, "=");
213 		s2 = strtok(NULL, "\n");
214 		if (!s1 || !s2)
215 			continue;
216 		if (!strcmp(s1, "UID")) {
217 			strcpy(USER, s2);
218 		} else if (!strcmp(s1, "SRV")) {
219 			strcpy(SERVER, s2);
220 		} else if (!strcmp(s1, "PWD")) {
221 			strcpy(PASSWORD, s2);
222 		} else if (!strcmp(s1, "DB")) {
223 			strcpy(DATABASE, s2);
224 		}
225 	}
226 	fclose(in);
227 
228 	Override:
229 	/* apply command-line overrides */
230 	if (options.username) {
231 		strcpy(USER, options.username);
232 		free(options.username);
233 	}
234 	if (options.password) {
235 		strcpy(PASSWORD, options.password);
236 		free(options.password);
237 	}
238 	if (options.servername) {
239 		strcpy(SERVER, options.servername);
240 		free(options.servername);
241 	}
242 	if (options.database) {
243 		strcpy(DATABASE, options.database);
244 		free(options.database);
245 	}
246 
247 	if (!*SERVER) {
248 		fprintf(stderr, "no servername provided, quitting.\n");
249 		exit(1);
250 	}
251 
252 	printf("found %s.%s for %s in \"%s\"\n", SERVER, DATABASE, USER, filename);
253 
254 #if 0
255 	dbrecftos(BASENAME);
256 #endif
257 	len = snprintf(sql_file, sizeof(sql_file), "%s/%s.sql", FREETDS_SRCDIR, BASENAME);
258 	assert(len >= 0 && len <= sizeof(sql_file));
259 
260 	if (input_file)
261 		fclose(input_file);
262 	if ((input_file = fopen(sql_file, "r")) == NULL) {
263 		fflush(stdout);
264 		fprintf(stderr, "could not open SQL input file \"%s\"\n", sql_file);
265 	}
266 
267 	if (!free_file_registered)
268 		atexit(free_file);
269 	free_file_registered = 1;
270 
271 	printf("SQL text will be read from %s\n", sql_file);
272 
273 	return 0;
274 }
275 
276 /*
277  * Fill the command buffer from a file while echoing it to standard output.
278  */
279 RETCODE
sql_cmd(DBPROCESS * dbproc)280 sql_cmd(DBPROCESS *dbproc)
281 {
282 	char line[2048], *p = line;
283 	int i = 0;
284 	RETCODE erc=SUCCEED;
285 
286 	if (!input_file) {
287 		fprintf(stderr, "%s: error: SQL input file \"%s\" not opened\n", BASENAME, sql_file);
288 		exit(1);
289 	}
290 
291 	while ((p = fgets(line, (int)sizeof(line), input_file)) != NULL && strcasecmp("go\n", p) != 0) {
292 		printf("\t%3d: %s", ++i, p);
293 		if ((erc = dbcmd(dbproc, p)) != SUCCEED) {
294 			fprintf(stderr, "%s: error: could write \"%s\" to dbcmd()\n", BASENAME, p);
295 			exit(1);
296 		}
297 	}
298 
299 	if (ferror(input_file)) {
300 		fprintf(stderr, "%s: error: could not read SQL input file \"%s\"\n", BASENAME, sql_file);
301 		exit(1);
302 	}
303 
304 	return erc;
305 }
306 
307 RETCODE
sql_rewind(void)308 sql_rewind(void)
309 {
310 	if (!input_file)
311 		return FAIL;
312 	rewind(input_file);
313 	return SUCCEED;
314 }
315 
316 RETCODE
sql_reopen(const char * fn)317 sql_reopen(const char *fn)
318 {
319 	int len = snprintf(sql_file, sizeof(sql_file), "%s/%s.sql", FREETDS_SRCDIR, fn);
320 	assert(len >= 0 && len <= sizeof(sql_file));
321 
322 	if (input_file)
323 		fclose(input_file);
324 	if ((input_file = fopen(sql_file, "r")) == NULL) {
325 		fflush(stdout);
326 		fprintf(stderr, "could not open SQL input file \"%s\"\n", sql_file);
327 		sql_file[0] = 0;
328 		return FAIL;
329 	}
330 	return SUCCEED;
331 }
332 
333 int
syb_msg_handler(DBPROCESS * dbproc,DBINT msgno,int msgstate,int severity,char * msgtext,char * srvname,char * procname,int line)334 syb_msg_handler(DBPROCESS * dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line)
335 {
336 	int *pexpected_msgno;
337 
338 	/*
339 	 * Check for "database changed", or "language changed" messages from
340 	 * the client.  If we get one of these, then we need to pull the
341 	 * name of the database or charset from the message and set the
342 	 * appropriate variable.
343 	 */
344 	if (msgno == 5701 ||	/* database context change */
345 	    msgno == 5703 ||	/* language changed */
346 	    msgno == 5704) {	/* charset changed */
347 
348 		/* fprintf( stderr, "msgno = %d: %s\n", msgno, msgtext ) ; */
349 		return 0;
350 	}
351 
352 	/*
353 	 * If the user data indicates this is an expected error message (because we're testing the
354 	 * error propogation, say) then indicate this message was anticipated.
355 	 */
356 	if (dbproc != NULL) {
357 		pexpected_msgno = (int *) dbgetuserdata(dbproc);
358 		if (pexpected_msgno && *pexpected_msgno == msgno) {
359 			fprintf(stdout, "OK: anticipated message arrived: %d %s\n", (int) msgno, msgtext);
360 			*pexpected_msgno = 0;
361 			return 0;
362 		}
363 	}
364 	/*
365 	 * If the severity is something other than 0 or the msg number is
366 	 * 0 (user informational messages).
367 	 */
368 	fflush(stdout);
369 	if (severity >= 0 || msgno == 0) {
370 		/*
371 		 * If the message was something other than informational, and
372 		 * the severity was greater than 0, then print information to
373 		 * stderr with a little pre-amble information.
374 		 */
375 		if (msgno > 0 && severity > 0) {
376 			fprintf(stderr, "Msg %d, Level %d, State %d\n", (int) msgno, (int) severity, (int) msgstate);
377 			fprintf(stderr, "Server '%s'", srvname);
378 			if (procname != NULL && *procname != '\0')
379 				fprintf(stderr, ", Procedure '%s'", procname);
380 			if (line > 0)
381 				fprintf(stderr, ", Line %d", line);
382 			fprintf(stderr, "\n");
383 			fprintf(stderr, "%s\n", msgtext);
384 			fflush(stderr);
385 		} else {
386 			/*
387 			 * Otherwise, it is just an informational (e.g. print) message
388 			 * from the server, so send it to stdout.
389 			 */
390 			fprintf(stdout, "%s\n", msgtext);
391 			fflush(stdout);
392 			severity = 0;
393 		}
394 	}
395 
396 	if (severity) {
397 		fprintf(stderr, "exit: no unanticipated messages allowed in unit tests\n");
398 		exit(EXIT_FAILURE);
399 	}
400 	return 0;
401 }
402 
403 int
syb_err_handler(DBPROCESS * dbproc,int severity,int dberr,int oserr,char * dberrstr,char * oserrstr)404 syb_err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
405 {
406 	int *pexpected_dberr;
407 
408 	/*
409 	 * For server messages, cancel the query and rely on the
410 	 * message handler to spew the appropriate error messages out.
411 	 */
412 	if (dberr == SYBESMSG)
413 		return INT_CANCEL;
414 
415 	/*
416 	 * If the user data indicates this is an expected error message (because we're testing the
417 	 * error propogation, say) then indicate this message was anticipated.
418 	 */
419 	if (dbproc != NULL) {
420 		pexpected_dberr = (int *) dbgetuserdata(dbproc);
421 		if (pexpected_dberr && *pexpected_dberr == dberr) {
422 			fprintf(stdout, "OK: anticipated error %d (%s) arrived\n", dberr, dberrstr);
423 			*pexpected_dberr = 0;
424 			return INT_CANCEL;
425 		}
426 	}
427 
428 	fflush(stdout);
429 	fprintf(stderr,
430 		"DB-LIBRARY error (dberr %d (severity %d): \"%s\"; oserr %d: \"%s\")\n",
431 		dberr, severity, dberrstr ? dberrstr : "(null)", oserr, oserrstr ? oserrstr : "(null)");
432 	fflush(stderr);
433 
434 	/*
435 	 * If the dbprocess is dead or the dbproc is a NULL pointer and
436 	 * we are not in the middle of logging in, then we need to exit.
437 	 * We can't do anything from here on out anyway.
438 	 * It's OK to end up here in response to a dbconvert() that
439 	 * resulted in overflow, so don't exit in that case.
440 	 */
441 	if ((dbproc == NULL) || DBDEAD(dbproc)) {
442 		if (dberr != SYBECOFL) {
443 			exit(255);
444 		}
445 	}
446 
447 	if (severity) {
448 		fprintf(stderr, "error: no unanticipated errors allowed in unit tests\n");
449 		exit(EXIT_FAILURE);
450 	}
451 
452 	return INT_CANCEL;
453 }
454