Client 3Getting Connection Parameters at Runtime

Client 3 Getting Connection Parameters at Runtime

Now we're ready to figure out how to do something smarter than using hardwired default connection parameters such as letting the user specify those values at runtime. The previous client programs have a significant shortcoming in that the connection parameters are written literally into the source code. To change any of those values, you have to edit the source file and recompile it. That's not very convenient, especially if you intend to make your program available for other people to use. One common way to specify connection parameters at runtime is by using command line options. For example, the programs in the MySQL distribution accept parameters in either of two forms, as shown in the following table.

Parameter Long Option Form Short Option Form
Hostname --host=host_name -h host_name
Username --user=user_name -u user_name
Password --password or -p or
  --password=your_pass -pyour_pass
Port number --port=port_num -P port_num
Socket name --socket=socket_name -S socket_name

For consistency with the standard MySQL clients, our next client program, client3, will accept those same formats. It's easy to do this because the client library includes support for option processing. In addition, our client will have the ability to extract information from option files, which allows you to put connection parameters in ~/.my.cnf (that is, the .my.cnf file in your home directory) or in any of the global option files. Then you don't have to specify the options on the command line each time you invoke the program. The client library makes it easy to check for MySQL option files and pull any relevant values from them. By adding only a few lines of code to your program, you can make it option file-aware, and you don't have to reinvent the wheel by writing your own code to do it. (Option file syntax is described in Appendix E, "MySQL Program Reference.")

Before writing client3 itself, we'll develop a couple programs that illustrate how MySQL's option-processing support works. These show how option handling works fairly simply and without the added complication of connecting to the MySQL server and processing queries.

Accessing Option File Contents

To read option files for connection parameter values, use the load_defaults() function. load_defaults() looks for option files, parses their contents for any option groups in which you're interested, and rewrites your program's argument vector (the argv[] array) to put information from those groups in the form of command line options at the beginning of argv[]. That way, the options appear to have been specified on the command line so that when you parse the command options, you get the connection parameters as part of your normal option-processing code. The options are added to argv[] immediately after the command name and before any other arguments (rather than at the end), so that any connection parameters specified on the command line occur later than and thus override any options added by load_defaults().

The following is a little program, show_argv, that demonstrates how to use load_defaults() and illustrates how it modifies your argument vector:

 /* show_argv.c - show effect of load_defaults() on argument vector */  #include <my_global.h> #include <mysql.h> static const char *client_groups[] = { "client", NULL }; int main (int argc, char *argv[]) { int i;     printf ("Original argument vector:\n");     for (i = 0; i < argc; i++)         printf ("arg %d: %s\n", i, argv[i]);     my_init ();     load_defaults ("my", client_groups, &argc, &argv);     printf ("Modified argument vector:\n");     for (i = 0; i < argc; i++)         printf ("arg %d: %s\n", i, argv[i]);     exit (0); } 

The option file-processing code involves several components:

  • client_groups[] is an array of character strings indicating the names of the option file groups from which you want to obtain options. Client programs normally include at least "client" in the list (which represents the [client] group), but you can list as many groups as you want. The last element of the array must be NULL to indicate where the list ends.

  • my_init() is an initialization routine that performs some setup operations required by load_defaults().

  • load_defaults() reads the option files. It takes four arguments: the prefix used in the names of your option files (this should always be "my"), the array listing the names of the option groups in which you're interested, and the addresses of your program's argument count and vector. Don't pass the values of the count and vector. Pass their addresses instead because load_defaults() needs to change their values. Note in particular that even though argv is already a pointer, you still pass &argv, that pointer's address.

show_argv prints its arguments twice to show the effect that load_defaults() has on the argument array. First it prints the arguments as they were specified on the command line, and then it calls load_defaults() and prints the argument array again.

To see how load_defaults() works, make sure you have a .my.cnf file in your home directory with some settings specified for the [client] group. (On Windows, you can use the C:\my.cnf file.) Suppose the file looks like this:

 [client]  user=sampadm password=secret host=some_host 

If that is the case, executing show_argv should produce output like this:

 % ./show_argv a b  Original argument vector: arg 0: ./show_argv arg 1: a arg 2: b Modified argument vector: arg 0: ./show_argv arg 1: --user=sampadm arg 2: --password=secret arg 3: --host=some_host arg 4: a arg 5: b 

When show_argv prints the argument vector the second time, the values in the option file show up as part of the argument list. It's also possible that you'll see some options that were not specified on the command line or in your ~/.my.cnf file. If this occurs, you will likely find that options for the [client] group are listed in a system-wide option file. This can happen because load_defaults() actually looks in several option files. On UNIX, it looks in /etc/my.cnf and in the my.cnf file in the MySQL data directory before reading .my.cnf in your home directory. On Windows, load_defaults() reads the my.ini file in your Windows system directory, C:\my.cnf, and the my.cnf file in the MySQL data directory.

Client programs that use load_defaults() almost always specify "client" in the list of option group names (so that they get any general client settings from option files), but you can set up your option file processing code to obtain options from other groups as well. Suppose you want show_argv to read options in both the [client] and [show_argv] groups. To accomplish this, find the following line in show_argv.c:

 const char *client_groups[] = { "client", NULL };  

Change the line to this:

 const char *client_groups[] = { "show_argv", "client", NULL };  

Then recompile show_argv, and the modified program will read options from both groups. To verify this, add a [show_argv] group to your ~/.my.cnf file:

 [client]  user=sampadm password=secret host=some_host [show_argv] host=other_host 

With these changes, invoking show_argv again will produce a different result than before:

 % ./show_argv a b  Original argument vector: arg 0: ./show_argv arg 1: a arg 2: b Modified argument vector: arg 0: ./show_argv arg 1: --user=sampadm arg 2: --password=secret arg 3: --host=some_host arg 4: --host=other_host arg 5: a arg 6: b 

The order in which option values appear in the argument array is determined by the order in which they are listed in your option file, not the order in which option group names are listed in the client_groups[] array. This means you'll probably want to specify program-specific groups after the [client] group in your option file. That way, if you specify an option in both groups, the program-specific value will take precedence over the more general [client] group value. You can see this in the example just shown; the host option was specified in both the [client] and [show_argv] groups, but because the [show_argv] group appears last in the option file, its host setting appears later in the argument vector and takes precedence.

load_defaults() does not pick up values from your environment settings. If you want to use the values of environment variables, such as MYSQL_TCP_PORT or MYSQL_UNIX_PORT, you must arrange for that yourself by using getenv(). I'm not going to add that capability to our clients, but what follows is a short code fragment that shows how to check the values of a couple of the standard MySQL-related environment variables:

 extern char *getenv();  char *p; int port_num = 0; char *socket_name = NULL; if ((p = getenv ("MYSQL_TCP_PORT")) != NULL)     port_num = atoi (p); if ((p = getenv ("MYSQL_UNIX_PORT")) != NULL)     socket_name = p; 

In the standard MySQL clients, environment variable values have lower precedence than values specified in option files or on the command line. If you check environment variables in your own programs and want to be consistent with that convention, check the environment before (not after) calling load_defaults() or processing command line options.

load_defaults() and Security

On multiple-user systems, utilities such as the ps program can display argument lists for arbitrary processes, including those being run by other users. Because of this, you may be wondering if there are any process-snooping implications of load_defaults() taking passwords that it finds in option files and putting them in your argument list. This actually is not a problem because ps displays the original argv[] contents. Any password argument created by load_defaults() points to an area of memory that it allocates for itself. That area is not part of the original vector, so ps never sees it.

On the other hand, a password that is given on the command line does show up in ps. This is one reason why it's not a good idea to specify passwords that way. One precaution a program can take to help reduce the risk is to remove the password from the argument list as soon as it starts executing. The next section, "Processing Command-Line Arguments," shows how to do that.

Processing Command-Line Arguments

Using load_defaults(), we can get all the connection parameters into the argument vector, but now we need a way to process the vector. The handle_options() function is designed for this. handle_options() is built into the MySQL client library, so you have access to it whenever you link in that library.

The option-processing methods described here were introduced in MySQL 4.0.2. Before that, the client library included option-handling code that was based on the getopt_long() function. If you're writing MySQL-based programs using the client library from a version of MySQL earlier than 4.0.2, you can use the version of this chapter from the first edition of this book, which describes how to process command options using getopt_long(). The first-edition chapter is available online in PDF format at the book's companion Web site at http://www.kitebird.com/mysql-book/.

The getopt_long()-based code has now been replaced with a new interface based on handle_options(). Some of the improvements offered by the new option-processing routines are:

  • More precise specification of the type and range of legal option values. For example, you can indicate not only that an option must have integer values but that it must be positive and a multiple of 1024.

  • Integration of help text, to make it easy to print a help message by calling a standard library function. There is no need to write your own special code to produce a help message.

  • Built in support for the standard --no-defaults, --print-defaults, --defaults-file, and --defaults-extra-file options. These options are described in the "Option Files" section in Appendix E.

  • Support for a standard set of option prefixes, such as --disable- and --enable-, to make it easier to implement boolean (on/off) options. These capabilities are not used in this chapter, but are described in the option-processing section of Appendix E.

Note: The new option-processing routines appeared in MySQL 4.0.2, but it's best to use 4.0.5 or later. Several problems were identified and fixed during the initial shaking-out period from 4.0.2 to 4.0.5.

To demonstrate how to use MySQL's option-handling facilities, this section describes a show_opt program that invokes load_defaults() to read option files and set up the argument vector and then processes the result using handle_options().

show_opt allows you to experiment with various ways of specifying connection parameters (whether in option files or on the command line) and to see the result by showing you what values would be used to make a connection to the MySQL server. show_opt is useful for getting a feel for what will happen in our next client program, client3, which hooks up this option-processing code with code that actually does connect to the server.

show_opt illustrates what happens at each phase of argument processing by performing the following actions:

  1. Set up default values for the hostname, username, password, and other connection parameters.

  2. Print the original connection parameter and argument vector values.

  3. Call load_defaults() to rewrite the argument vector to reflect option file contents and then print the resulting vector.

  4. Call the option processing routine handle_options() to process the argument vector and then print the resulting connection parameter values and whatever is left in the argument vector.

The following discussion explains how show_opt works, but first take a look at its source file, show_opt.c:

 /*   * show_opt.c - demonstrate option processing with load_defaults()  * and handle_options()  */ #include <my_global.h> #include <mysql.h> #include <my_getopt.h> static char *opt_host_name = NULL;      /* server host (default=localhost) */ static char *opt_user_name = NULL;      /* username (default=login name) */ static char *opt_password = NULL;       /* password (default=none) */ static unsigned int opt_port_num = 0;   /* port number (use built-in value) */ static char *opt_socket_name = NULL;    /* socket name (use built-in value) */ static const char *client_groups[] = { "client", NULL }; static struct my_option my_opts[] =     /* option information structures */ {     {"help", '?', "Display this help and exit",     NULL, NULL, NULL,     GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},     {"host", 'h', "Host to connect to",     (gptr *) &opt_host_name, NULL, NULL,     GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},     {"password", 'p', "Password",     (gptr *) &opt_password, NULL, NULL,     GET_STR_ALLOC, OPT_ARG, 0, 0, 0, 0, 0, 0},     {"port", 'P', "Port number",     (gptr *) &opt_port_num, NULL, NULL,     GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},     {"socket", 'S', "Socket path",     (gptr *) &opt_socket_name, NULL, NULL,     GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},     {"user", 'u', "User name",     (gptr *) &opt_user_name, NULL, NULL,     GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},     { NULL, 0, NULL, NULL, NULL, NULL, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 } }; my_bool get_one_option (int optid, const struct my_option *opt, char *argument) {     switch (optid)     {     case '?':         my_print_help (my_opts);    /* print help message */         exit (0);     }     return (0); } int main (int argc, char *argv[]) { int i; int opt_err;     printf ("Original connection parameters:\n");     printf ("host name: %s\n", opt_host_name ? opt_host_name : "(null)");     printf ("user name: %s\n", opt_user_name ? opt_user_name : "(null)");     printf ("password: %s\n", opt_password ? opt_password : "(null)");     printf ("port number: %u\n", opt_port_num);     printf ("socket name: %s\n", opt_socket_name ? opt_socket_name : "(null)");     printf ("Original argument vector:\n");     for (i = 0; i < argc; i++)         printf ("arg %d: %s\n", i, argv[i]);     my_init ();     load_defaults ("my", client_groups, &argc, &argv);     printf ("Modified argument vector after load_defaults():\n");     for (i = 0; i < argc; i++)         printf ("arg %d: %s\n", i, argv[i]);     if ((opt_err = handle_options (&argc, &argv, my_opts, get_one_option)))         exit (opt_err);     printf ("Connection parameters after handle_options():\n");     printf ("host name: %s\n", opt_host_name ? opt_host_name : "(null)");     printf ("user name: %s\n", opt_user_name ? opt_user_name : "(null)");     printf ("password: %s\n", opt_password ? opt_password : "(null)");     printf ("port number: %u\n", opt_port_num);     printf ("socket name: %s\n", opt_socket_name ? opt_socket_name : "(null)");     printf ("Argument vector after handle_options():\n");     for (i = 0; i < argc; i++)         printf ("arg %d: %s\n", i, argv[i]);     exit (0); } 

The option-processing approach illustrated by show_opt.c involves the following aspects, which will be common to any program that uses the MySQL client library to handle command options:

  1. In addition to the my_global.h and mysql.h header files, include my_getopt.h as well. my_getopt.h defines the interface to MySQL's option-processing facilities.

  2. Define an array of my_option structures. In show_opt.c, this array is named my_opts. The array should have one structure per option that the program understands. Each structure provides information such as an option's short and long names, its default value, whether the value is a number or string, and so on. Details on members of the my_option structure are provided shortly.

  3. After calling load_defaults() to read the option files and set up the argument vector, process the options by calling handle_options(). The first two arguments to handle_options() are the addresses of your program's argument count and vector. (Just as with load_options(), you pass the addresses of these variables, not their values.) The third argument points to the array of my_option structures. The fourth argument is a pointer to a helper function. The handle_options() routine and the my_options structures are designed to make it possible for most option-processing actions to be performed automatically for you by the client library. However, to allow for special actions that the library does not handle, your program should also define a helper function for handle_options() to call. In show_opt.c, this function is named get_one_option(). The operation of the helper function is described shortly.

The my_option structure defines the types of information that must be specified for each option that the program understands. It looks like this:

 struct my_option  {   const char *name;               /* option's long name */   int        id;                  /* option's short name or code */   const char *comment;            /* option description for help message */   gptr       *value;              /* pointer to variable to store value in */   gptr       *u_max_value;        /* The user defined max variable value */   const char **str_values;        /* array of legal option values (unused) */   enum get_opt_var_type var_type; /* option value's type */   enum get_opt_arg_type arg_type; /* whether option value is required */   longlong   def_value;           /* option's default value */   longlong   min_value;           /* option's minimum allowable value */   longlong   max_value;           /* option's maximum allowable value */   longlong   sub_size;            /* amount to shift value by */   long       block_size;          /* option value multiplier */   int        app_type;            /* reserved for application-specific use */ }; 

The members of the my_option structure are used as follows:

  • name

    The long option name. This is the --name form of the option, without the leading dashes. For example, if the long option is --user, list it as "user" in the my_option structure.

  • id

    The short (single-letter) option name, or a code value associated with the option if it has no single-letter name. For example, if the short option is -u, list it as 'u' in the my_option structure. For options that have only a long name and no corresponding single-character name, you should make up a set of option code values to be used internally for the short names. The values must be unique and different than all the single-character names. (To satisfy the latter constraint, make the codes greater than 255, the largest possible single-character value. An example of this technique is shown in "Writing Clients That Include SSL Support" section later in this chapter.)

  • comment

    An explanatory string that describes the purpose of the option. This is the text that you want displayed in a help message.

  • value

    This is a gptr (generic pointer) value. It points to the variable where you want the option's argument to be stored. After the options have been processed, you can check that variable to see what the option's value has been set to. If the option takes no argument, value can be NULL. Otherwise, the data type of the variable that's pointed to must be consistent with the value of the var_type member.

  • u_max_value

    This is another gptr value, but it's used only by the server. For client programs, set u_max_value to NULL.

  • str_values

    This member currently is unused. In future MySQL releases, it might be used to allow a list of legal values to be specified, in which case any option value given will be required to match one of these values.

  • var_type

    This member indicates what kind of value must follow the option name on the command line and can be any of the following:

    var_type Value Meaning
    GET_NO_ARG No value
    GET_BOOL Boolean value
    GET_INT Integer value
    GET_UINT Unsigned integer value
    GET_LONG Long integer value
    GET_ULONG Unsigned long integer value
    GET_LL Long long integer value
    GET_ULL Unsigned long long integer value
    GET_STR String value
    GET_STR_ALLOC String value

    The difference between GET_STR and GET_STR_ALLOC is that for GET_STR, the option variable will be set to point directly at the value in the argument vector, whereas for GET_STR_ALLOC, a copy of the argument will be made and the option variable will be set to point to the copy.

  • arg_type

    The arg_type value indicates whether a value follows the option name and can be any of the following:

    arg_type Value Meaning
    NO_ARG Option takes no following argument
    OPT_ARG Option may take a following argument
    REQUIRED_ARG Option requires a following argument

    If arg_type is NO_ARG, then var_type should be set to GET_NO_ARG.

  • def_value

    For numeric-valued options, the option will be assigned this value by default if no explicit value is specified in the argument vector.

  • min_value

    For numeric-valued options, this is the smallest value that can be specified. Smaller values are bumped up to this value automatically. Use 0 to indicate "no minimum."

  • max_value

    For numeric-valued options, this is the largest value that can be specified. Larger values are bumped down to this value automatically. Use 0 to indicate "no maximum."

  • sub_size

    For numeric-valued options, sub_size is an offset that is used to convert values from the range as given in the argument vector to the range that is used internally. For example, if values are given on the command line in the range from 1 to 256, but the program wants to use an internal range of 0 to 255, set sub_size to 1.

  • block_size

    For numeric-valued options, if this value is non-zero, it indicates a block size. Option values will be rounded down to the nearest multiple of this size if necessary. For example, if values must be even, set the block size to 2; handle_options() will round odd values down to the nearest even number.

  • app_type

    This is reserved for application-specific use.

The my_opts array should have a my_option structure for each valid option, followed by a terminating structure that is set up as follows to indicate the end of the array:

 { NULL, 0, NULL, NULL, NULL, NULL, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }  

When you invoke handle_options() to process the argument vector, it skips over the first argument (the program name) and then processes option arguments that is, arguments that begin with a dash. This continues until it reaches the end of the vector or encounters the special "end of options" argument ('--' by itself). As it moves through the argument vector, handle_options() calls the helper function once per option to allow that function to perform any special processing. handle_options() passes three arguments to the helper function the short option value, a pointer to the option's my_option structure, and a pointer to the argument that follows the option in the argument vector (which will be NULL if the option is specified without a following value).

When handle_options() returns, the argument count and vector will have been reset appropriately to represent an argument list containing only the non-option arguments.

The following is a sample invocation of show_opt and the resulting output (assuming that ~/.my.cnf still has the same contents as for the final show_argv example in the "Accessing Option File Contents" section earlier in this chapter):

 % ./show_opt -h yet_another_host --user=bill x  Original connection parameters: host name: (null) user name: (null) password: (null) port number: 0 socket name: (null) Original argument vector: arg 0: ./show_opt arg 1: -h arg 3: yet_another_host arg 3: --user=bill arg 4: x Modified argument vector after load_defaults(): arg 0: ./show_opt arg 1: --user=sampadm arg 2: --password=secret arg 3: --host=some_host arg 4: -h arg 5: yet_another_host arg 6: --user=bill arg 7: x Connection parameters after handle_options(): host name: yet_another_host user name: bill password: secret port number: 0 socket name: (null) Argument vector after handle_options(): arg 0: x 

The output shows that the hostname is picked up from the command line (overriding the value in the option file) and that the username and password come from the option file. handle_options() correctly parses options whether specified in short-option form (such as -h yet_another_host) or in long-option form (such as --user=bill).

The get_one_option() helper function is used in conjunction with handle_options(). For show_opt, it is fairly minimal and takes no action except for the --help or -? options (for which handle_options() passes an optid value of '?'):

 my_bool  get_one_option (int optid, const struct my_option *opt, char *argument) {     switch (optid)     {     case '?':         my_print_help (my_opts);    /* print help message */         exit (0);     }     return (0); } 

my_print_help() is a client library routine that automatically produces a help message for you, based on the option names and comment strings in the my_opts array. To see how it works, try the following command; the final part of the output will be the help message:

 % ./show_opt --help  

You can add other cases to get_one_option() as necessary. For example, this function is useful for handling password options. When you specify such an option, the password value may or may not be given, as indicated by OPT_ARG in the option information structure. (That is, you can specify the option as --password or --password=your_pass if you use the long-option form or as -p or -pyour_pass if you use the short-option form.) MySQL clients typically allow you to omit the password value on the command line and then prompt you for it. This allows you to avoid giving the password on the command line, which keeps people from seeing your password. In later programs, we'll use get_one_option() to check whether or not a password value was given. We'll save the value if so, and, otherwise, set a flag to indicate that the program should prompt the user for a password before attempting to connect to the server.

You may find it instructive to modify the option structures in show_opt.c to see how your changes affect the program's behavior. For example, if you set the minimum, maximum, and block size values for the --port option to 100, 1000, and 25, you'll find after recompiling the program that you cannot set the port number to a value outside the range from 100 to 1000 and that values get rounded down automatically to the nearest multiple of 25.

The option processing routines also handle the --no-defaults, --print-defaults, --defaults-file, and --defaults-extra-file options automatically. Try invoking show_opt with each of these options to see what happens.

Incorporating Option Processing into a MySQL Client Program

Now let's strip out from show_opt.c the stuff that's purely illustrative of how the option-handling routines work and use the remainder as a basis for a client that connects to a server according to any options that are provided in an option file or on the command line. The resulting source file, client3.c, is as follows:

 /*   * client3.c - connect to MySQL server, using connection parameters  * specified in an option file or on the command line  */ #include <string.h>     /* for strdup() */ #include <my_global.h> #include <mysql.h> #include <my_getopt.h> static char *opt_host_name = NULL;      /* server host (default=localhost) */ static char *opt_user_name = NULL;      /* username (default=login name) */ static char *opt_password = NULL;       /* password (default=none) */ static unsigned int opt_port_num = 0;   /* port number (use built-in value) */ static char *opt_socket_name = NULL;    /* socket name (use built-in value) */ static char *opt_db_name = NULL;        /* database name (default=none) */ static unsigned int opt_flags = 0;      /* connection flags (none) */ static int ask_password = 0;            /* whether to solicit password */ static MYSQL *conn;                     /* pointer to connection handler */ static const char *client_groups[] = { "client", NULL }; static struct my_option my_opts[] =     /* option information structures */ {     {"help", '?', "Display this help and exit",     NULL, NULL, NULL,     GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},     {"host", 'h', "Host to connect to",     (gptr *) &opt_host_name, NULL, NULL,     GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},     {"password", 'p', "Password",     (gptr *) &opt_password, NULL, NULL,     GET_STR_ALLOC, OPT_ARG, 0, 0, 0, 0, 0, 0},     {"port", 'P', "Port number",     (gptr *) &opt_port_num, NULL, NULL,     GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},     {"socket", 'S', "Socket path",     (gptr *) &opt_socket_name, NULL, NULL,     GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},     {"user", 'u', "User name",     (gptr *) &opt_user_name, NULL, NULL,     GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},     { NULL, 0, NULL, NULL, NULL, NULL, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 } }; void print_error (MYSQL *conn, char *message) {     fprintf (stderr, "%s\n", message);     if (conn != NULL)     {         fprintf (stderr, "Error %u (%s)\n",                 mysql_errno (conn), mysql_error (conn));     } } my_bool get_one_option (int optid, const struct my_option *opt, char *argument) {     switch (optid)     {     case '?':         my_print_help (my_opts);    /* print help message */         exit (0);     case 'p':                       /* password */         if (!argument)              /* no value given, so solicit it later */             ask_password = 1;         else                        /* copy password, wipe out original */         {             opt_password = strdup (argument);             if (opt_password == NULL)             {                 print_error (NULL, "could not allocate password buffer");                 exit (1);             }             while (*argument)                 *argument++ = 'x';         }         break;     }     return (0); } int main (int argc, char *argv[]) { int opt_err;     my_init ();     load_defaults ("my", client_groups, &argc, &argv);     if ((opt_err = handle_options (&argc, &argv, my_opts, get_one_option)))         exit (opt_err);     /* solicit password if necessary */     if (ask_password)         opt_password = get_tty_password (NULL);     /* get database name if present on command line */     if (argc > 0)     {         opt_db_name = argv[0];         --argc; ++argv;     }     /* initialize connection handler */     conn = mysql_init (NULL);     if (conn == NULL)     {         print_error (NULL, "mysql_init() failed (probably out of memory)");         exit (1);     }     /* connect to server */     if (mysql_real_connect (conn, opt_host_name, opt_user_name, opt_password,             opt_db_name, opt_port_num, opt_socket_name, opt_flags) == NULL)     {         print_error (conn, "mysql_real_connect() failed");         mysql_close (conn);         exit (1);     }     /* ... issue queries and process results here ... */     /* disconnect from server */     mysql_close (conn);     exit (0); } 

Compared to the client1, client2, and show_opt programs that we developed earlier, client3 does a few new things:

  • It allows a database to be selected on the command line; just specify the database after the other arguments. This is consistent with the behavior of the standard clients in the MySQL distribution.

  • If a password value is present in the argument vector, get_one_option() makes a copy of it and then wipes out the original. This minimizes the time window during which a password specified on the command line is visible to ps or to other system status programs. (The window is only minimized, not eliminated. Specifying passwords on the command line still is a security risk.)

  • If a password option was given without a value, get_one_option() sets a flag to indicate that the program should prompt the user for a password. That's done in main() after all options have been processed, using the get_tty_password() function. This is a utility routine in the client library that prompts for a password without echoing it on the screen. You may ask, "Why not just call getpass()?" The answer is that not all systems have that function Windows, for example. get_tty_password() is portable across systems because it's configured to adjust to system idiosyncrasies.

client3 connects to the MySQL server according to the options you specify. Assume there is no option file to complicate matters. If you invoke client3 with no arguments, it connects to localhost and passes your UNIX login name and no password to the server. If instead you invoke client3 as shown in the following command, it prompts for a password (because there is no password value immediately following -p), connects to some_host, and passes the username some_user to the server as well as the password you type:

 % ./client3 -h some_host -p -u some_user some_db  

client3 also passes the database name some_db to mysql_real_connect() to make that the current database. If there is an option file, its contents are processed and used to modify the connection parameters accordingly.

The work we've done so far to produce client3 accomplishes something that's necessary for every MySQL client connecting to the server using appropriate parameters. The process is implemented by the client skeleton, client3.c, which you can use as the basis for other programs. Copy it and add to it any application-specific details. That means you can concentrate more on what you're really interested in being able to access the content of your databases. All the real action for your application will take place between the mysql_real_connect() and mysql_close() calls, but what we have now serves as a basic framework that you can use for many different clients. To write a new program, just do the following:

  1. Make a copy of client3.c.

  2. Modify the option-processing loop if you accept additional options other than the standard ones that client3.c knows about.

  3. Add your own application-specific code between the connect and disconnect calls.

And you're done.



MySQL
High Performance MySQL: Optimization, Backups, Replication, and More
ISBN: 0596101716
EAN: 2147483647
Year: 2003
Pages: 188

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net