LCOV - code coverage report
Current view: top level - src/dblib/unittests - common.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 89 145 61.4 %
Date: 2025-07-02 09:40:27 Functions: 8 8 100.0 %

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

Generated by: LCOV version 1.13