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