1 /*
2    Copyright (c) 2012, 2021, Oracle and/or its affiliates.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 /**
26   @file
27 
28   @brief
29   MySQL Configuration Utility
30 */
31 
32 /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
33 #include "my_config.h"
34 #include <welcome_copyright_notice.h>
35 #include <signal.h>
36 #include <my_dir.h>
37 #include <my_rnd.h>
38 #include "my_aes.h"
39 #include "client_priv.h"
40 #include "my_default.h"
41 #include "my_default_priv.h"
42 
43 #define MYSQL_CONFIG_EDITOR_VERSION "1.0"
44 #define MY_LINE_MAX 4096
45 #define MAX_COMMAND_LIMIT 100
46 /*
47   Header length for the login file.
48   4-byte (unused) + LOGIN_KEY_LEN
49  */
50 #define MY_LOGIN_HEADER_LEN (4 + LOGIN_KEY_LEN)
51 
52 static int g_fd;
53 
54 /*
55   Length of the contents in login file
56   excluding the header part.
57 */
58 static size_t file_size;
59 static const char *opt_user= NULL, *opt_password= NULL, *opt_host=NULL,
60             *opt_login_path= "client", *opt_socket= NULL, *opt_port= NULL;
61 
62 static char my_login_file[FN_REFLEN];
63 static char my_key[LOGIN_KEY_LEN];
64 
65 static my_bool opt_verbose, opt_all, tty_password= 0, opt_warn,
66                opt_remove_host, opt_remove_pass, opt_remove_user,
67                opt_remove_socket, opt_remove_port, login_path_specified= FALSE;
68 
69 static int execute_commands(int command);
70 static int set_command(void);
71 static int remove_command(void);
72 static int print_command(void);
73 static void print_login_path(DYNAMIC_STRING *file_buf, const char *path_name);
74 static void remove_login_path(DYNAMIC_STRING *file_buf, const char *path_name);
75 static char* locate_login_path(DYNAMIC_STRING *file_buf, const char *path_name);
76 static my_bool check_and_create_login_file(void);
77 static void mask_password_and_print(char *buf);
78 static int reset_login_file(bool gen_key);
79 
80 static int encrypt_buffer(const char *plain, int plain_len, char cipher[], const int aes_len);
81 static int decrypt_buffer(const char *cipher, int cipher_len, char plain[]);
82 static int encrypt_and_write_file(DYNAMIC_STRING *file_buf);
83 static int read_and_decrypt_file(DYNAMIC_STRING *file_buf);
84 static int do_handle_options(int argc, char *argv[]);
85 static void remove_options(DYNAMIC_STRING *file_buf, const char *path_name);
86 static void remove_option(DYNAMIC_STRING *file_buf, const char *path_name,
87                           const char* option_name);
88 void generate_login_key(void);
89 static int read_login_key(void);
90 static int add_header(void);
91 static void my_perror(const char *msg);
92 
93 static void verbose_msg(const char *fmt, ...);
94 static void print_version(void);
95 static void usage_program(void);
96 static void usage_command(int command);
97 extern "C" my_bool get_one_option(int optid, const struct my_option *opt,
98                                   char *argument);
99 
100 enum commands {
101   MY_CONFIG_SET,
102   MY_CONFIG_REMOVE,
103   MY_CONFIG_PRINT,
104   MY_CONFIG_RESET,
105   MY_CONFIG_HELP
106 };
107 
108 struct my_command_data {
109   const int id;
110   const char *name;
111   const char *description;
112   my_option *options;
113   my_bool (*get_one_option_func)(int optid,
114                                  const struct my_option *opt,
115                                  char *argument);
116 };
117 
118 
119 /* mysql_config_editor utility options. */
120 static struct my_option my_program_long_options[]=
121 {
122 #ifdef NDEBUG
123   {"debug", '#', "This is a non-debug version. Catch this and exit.",
124   0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
125 #else
126   {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
127    0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
128 #endif
129   {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
130    NO_ARG, 0, 0, 0, 0, 0, 0},
131   {"verbose", 'v', "Write more information.", &opt_verbose,
132    &opt_verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
133   {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG,
134    NO_ARG, 0, 0, 0, 0, 0, 0},
135   {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
136 };
137 
138 /* Command-specific options. */
139 
140 /* SET command options. */
141 static struct my_option my_set_command_options[]=
142 {
143   {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
144    NO_ARG, 0, 0, 0, 0, 0, 0},
145   {"host", 'h', "Host name to be entered into the login file.", &opt_host,
146    &opt_host, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
147   {"login-path", 'G', "Name of the login path to use in the login file. "
148    "(Default : client)", &opt_login_path, &opt_login_path, 0, GET_STR,
149    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
150   {"password", 'p', "Prompt for password to be entered into the login file.",
151    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
152   {"user", 'u', "User name to be entered into the login file.", &opt_user,
153    &opt_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
154   {"socket", 'S', "Socket path to be entered into login file.", &opt_socket,
155    &opt_socket, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
156   {"port", 'P', "Port number to be entered into login file.", &opt_port,
157    &opt_port, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
158   {"warn", 'w', "Warn and ask for confirmation if set command attempts to "
159    "overwrite an existing login path (enabled by default).",
160    &opt_warn, &opt_warn, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
161   {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
162 };
163 
164 /* REMOVE command options. */
165 static struct my_option my_remove_command_options[]=
166 {
167   {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
168    NO_ARG, 0, 0, 0, 0, 0, 0},
169   {"host", 'h', "Remove host name from the login path.",
170    &opt_remove_host, &opt_remove_host, 0, GET_BOOL, NO_ARG, 0, 0, 0,
171    0, 0, 0},
172   {"login-path", 'G', "Name of the login path from which options to "
173    "be removed (entire path would be removed if none of user, password, "
174    "host, socket, or port options are specified). (Default : client)",
175    &opt_login_path, &opt_login_path, 0, GET_STR, REQUIRED_ARG,
176    0, 0, 0, 0, 0, 0},
177   {"password", 'p', "Remove password from the login path.",
178    &opt_remove_pass, &opt_remove_pass, 0, GET_BOOL, NO_ARG, 0, 0, 0,
179    0, 0, 0},
180   {"user", 'u', "Remove user name from the login path.", &opt_remove_user,
181    &opt_remove_user, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
182   {"warn", 'w', "Warn and ask for confirmation if remove command attempts "
183    "to remove the default login path (client) if no login path is specified "
184    "(enabled by default).", &opt_warn, &opt_warn, 0, GET_BOOL, NO_ARG, 1,
185    0, 0, 0, 0, 0},
186   {"socket", 'S', "Remove socket path from the login path.", &opt_remove_socket,
187    &opt_remove_socket, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
188   {"port", 'P', "Remove port number from the login path.", &opt_remove_port,
189    &opt_remove_port, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
190   {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
191 };
192 
193 /* PRINT command options. */
194 static struct my_option my_print_command_options[]=
195 {
196   {"all", OPT_CONFIG_ALL, "Used with print command to print all login paths.",
197    &opt_all, &opt_all, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
198   {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
199    NO_ARG, 0, 0, 0, 0, 0, 0},
200   {"login-path", 'G', "Name of the login path to use in the login file. "
201    "(Default : client)", &opt_login_path, &opt_login_path, 0, GET_STR,
202    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
203   {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
204 };
205 
206 /* RESET command options. */
207 static struct my_option my_reset_command_options[]=
208 {
209   {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG,
210    NO_ARG, 0, 0, 0, 0, 0, 0},
211   {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
212 };
213 
214 /* HELP command options. */
215 static struct my_option my_help_command_options[]=
216 {
217   {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
218 };
219 
220 my_bool
my_program_get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)221 my_program_get_one_option(int optid,
222                           const struct my_option *opt MY_ATTRIBUTE((unused)),
223                           char *argument)
224 {
225   switch(optid) {
226   case '#':
227     DBUG_PUSH(argument ? argument : "d:t:o,/tmp/mysql_config_editor.trace");
228     break;
229   case 'V':
230     print_version();
231     exit(0);
232     break;
233   case '?':
234     usage_program();
235     exit(0);
236     break;
237   }
238   return 0;
239 }
240 
241 my_bool
my_set_command_get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)242 my_set_command_get_one_option(int optid,
243                               const struct my_option *opt MY_ATTRIBUTE((unused)),
244                               char *argument)
245 {
246   switch(optid) {
247   case 'p':
248     tty_password= 1;
249     break;
250   case 'G':
251     if (login_path_specified)
252     {
253       /* Error, we do not support multiple login paths. */
254       my_perror("Error: Use of multiple login paths is not supported. "
255                 "Exiting..");
256       return 1;
257     }
258     login_path_specified= TRUE;
259     break;
260   case '?':
261     usage_command(MY_CONFIG_SET);
262     exit(0);
263     break;
264   }
265   return 0;
266 }
267 
268 my_bool
my_remove_command_get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)269 my_remove_command_get_one_option(int optid,
270                                  const struct my_option *opt MY_ATTRIBUTE((unused)),
271                                  char *argument)
272 {
273   switch(optid) {
274   case 'G':
275     if (login_path_specified)
276     {
277       /* Error, we do not support multiple login paths. */
278       my_perror("Error: Use of multiple login paths is not supported. "
279                 "Exiting..");
280       return 1;
281     }
282     login_path_specified= TRUE;
283     break;
284   case '?':
285     usage_command(MY_CONFIG_REMOVE);
286     exit(0);
287     break;
288   }
289   return 0;
290 }
291 
292 my_bool
my_print_command_get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)293 my_print_command_get_one_option(int optid,
294                                 const struct my_option *opt MY_ATTRIBUTE((unused)),
295                                 char *argument)
296 {
297   switch(optid) {
298   case 'G':
299     if (login_path_specified)
300     {
301       /* Error, we do not support multiple login paths. */
302       my_perror("Error: Use of multiple login paths is not supported. "
303                 "Exiting..");
304       return 1;
305     }
306     login_path_specified= TRUE;
307     break;
308   case '?':
309     usage_command(MY_CONFIG_PRINT);
310     exit(0);
311     break;
312   }
313   return 0;
314 }
315 
316 my_bool
my_reset_command_get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)317 my_reset_command_get_one_option(int optid,
318                                 const struct my_option *opt MY_ATTRIBUTE((unused)),
319                                 char *argument)
320 {
321   switch(optid) {
322   case '?':
323     usage_command(MY_CONFIG_RESET);
324     exit(0);
325     break;
326   }
327   return 0;
328 }
329 
330 static struct my_command_data command_data[]=
331 {
332   {
333     MY_CONFIG_SET,
334     "set",
335     "Write a login path to the login file.",
336     my_set_command_options,
337     my_set_command_get_one_option
338   },
339   {
340     MY_CONFIG_REMOVE,
341     "remove",
342     "Remove a login path from the login file.",
343     my_remove_command_options,
344     my_remove_command_get_one_option
345   },
346   {
347     MY_CONFIG_PRINT,
348     "print",
349     "Print the contents of login file in unencrypted form.",
350     my_print_command_options,
351     my_print_command_get_one_option
352   },
353   {
354     MY_CONFIG_RESET,
355     "reset",
356     "Empty the contents of the login file. The file is created\n"
357     "if it does not exist.",
358     my_reset_command_options,
359     my_reset_command_get_one_option
360   },
361   {
362     MY_CONFIG_HELP,
363     "help",
364     "Display a help message and exit.",
365     my_help_command_options,
366     NULL
367   },
368   {
369     0, NULL, NULL, NULL, NULL
370   }
371 };
372 
373 
main(int argc,char * argv[])374 int main(int argc, char *argv[])
375 {
376   MY_INIT(argv[0]);
377   DBUG_ENTER("main");
378   int command, rc= 0;
379 
380   command= do_handle_options(argc, argv);
381 
382   if (command > -1)
383     rc= execute_commands(command);
384 
385   if (rc != 0)
386   {
387     my_perror("operation failed.");
388     DBUG_RETURN(1);
389   }
390   DBUG_RETURN(0);
391 }
392 
393 
394 /**
395   Handle all the command line arguments.
396 
397   program_name [program options] [command [command options]]
398 
399 */
do_handle_options(int argc,char * argv[])400 static int do_handle_options(int argc, char *argv[])
401 {
402   DBUG_ENTER("do_handle_options");
403 
404   const char *command_list[MAX_COMMAND_LIMIT + 1];
405   char **saved_argv= argv;
406   char **argv_cmd;
407   char *ptr;                                    /* for free. */
408   int argc_cmd;
409   int rc, i, command= -1;
410 
411   if (argc < 2)
412   {
413     usage_program();
414     exit(1);
415   }
416 
417   if (!(ptr= (char *) my_malloc(PSI_NOT_INSTRUMENTED,
418                                 (argc + 2) * sizeof(char *),
419                                 MYF(MY_WME))))
420     goto error;
421 
422   /* Handle program options. */
423 
424   /* Prepare a list of supported commands to be used by my_handle_options(). */
425   for (i= 0; (command_data[i].name != NULL) && (i < MAX_COMMAND_LIMIT); i++)
426     command_list[i]= (char *) command_data[i].name;
427   command_list[i]= NULL;
428 
429   if ((rc= my_handle_options(&argc, &argv, my_program_long_options,
430                              my_program_get_one_option, command_list, FALSE)))
431     exit(rc);
432 
433   if (argc == 0)                                /* No command specified. */
434     goto done;
435 
436   for (i= 0; command_data[i].name != NULL; i++)
437   {
438     if (!strcmp(command_data[i].name, argv[0]))
439     {
440       command= i;
441       break;
442     }
443   }
444 
445   if (command == -1)
446     goto error;
447 
448   /* Handle command options. */
449 
450   argv_cmd= (char **)ptr;
451   argc_cmd= argc + 1;
452 
453   /* Prepare a command line (argv) using the rest of the options. */
454   argv_cmd[0]= saved_argv[0];
455   memcpy((uchar *) (argv_cmd + 1), (uchar *) (argv),
456          (argc * sizeof(char *)));
457   argv_cmd[argc_cmd]= 0;
458 
459   if ((rc= handle_options(&argc_cmd, &argv_cmd,
460                           command_data[command].options,
461                           command_data[command].get_one_option_func)))
462     exit(rc);
463 
464   /* Do not allow multiple commands. */
465   if ( argc_cmd > 1)
466     goto error;
467 
468 done:
469   my_free(ptr);
470   DBUG_RETURN(command);
471 
472 error:
473   my_free(ptr);
474   usage_program();
475   exit(1);
476 }
477 
478 
execute_commands(int command)479 static int execute_commands(int command)
480 {
481   DBUG_ENTER("execute_commands");
482   int rc= 0;
483 
484   if ((rc= check_and_create_login_file()))
485     goto done;
486 
487   switch(command_data[command].id) {
488     case MY_CONFIG_SET :
489       verbose_msg("Executing set command.\n");
490       rc= set_command();
491       break;
492 
493     case MY_CONFIG_REMOVE :
494       verbose_msg("Executing remove command.\n");
495       rc= remove_command();
496       break;
497 
498     case MY_CONFIG_PRINT :
499       verbose_msg("Executing print command.\n");
500       rc= print_command();
501      break;
502 
503     case MY_CONFIG_RESET :
504       verbose_msg("Resetting login file.\n");
505       rc= reset_login_file(1);
506       break;
507 
508     case MY_CONFIG_HELP :
509       verbose_msg("Printing usage info.\n");
510       usage_program();
511       break;
512 
513     default :
514       my_perror("unknown command.");
515       exit(1);
516   }
517 
518 done:
519   my_close(g_fd, MYF(MY_WME));
520 
521   DBUG_RETURN(rc);
522 }
523 
524 
525 /**
526   Execute 'set' command.
527 
528   @param void
529 
530   @return -1              Error
531            0              Success
532 */
533 
set_command(void)534 static int set_command(void)
535 {
536   DBUG_ENTER("set_command");
537 
538   DYNAMIC_STRING file_buf, path_buf;
539 
540   init_dynamic_string(&path_buf, "", MY_LINE_MAX, MY_LINE_MAX);
541   init_dynamic_string(&file_buf, "", file_size, 3 * MY_LINE_MAX);
542 
543   if (tty_password)
544     opt_password= get_tty_password(NullS);
545 
546   if (file_size)
547   {
548     if (read_and_decrypt_file(&file_buf) == -1)
549       goto error;
550   }
551 
552   dynstr_append(&path_buf, "[");                /* --login=path */
553   if (opt_login_path)
554     dynstr_append(&path_buf, opt_login_path);
555   else
556     dynstr_append(&path_buf, "client");
557   dynstr_append(&path_buf, "]");
558 
559   if (opt_user)                                 /* --user */
560   {
561     dynstr_append(&path_buf, "\nuser = ");
562     dynstr_append(&path_buf, opt_user);
563   }
564 
565   if (opt_password)                             /* --password */
566   {
567     dynstr_append(&path_buf, "\npassword = ");
568     dynstr_append(&path_buf, opt_password);
569   }
570 
571   if (opt_host)                                 /* --host */
572   {
573     dynstr_append(&path_buf, "\nhost = ");
574     dynstr_append(&path_buf, opt_host);
575   }
576 
577   if (opt_socket)
578   {
579     dynstr_append(&path_buf, "\nsocket = ");
580     dynstr_append(&path_buf, opt_socket);
581   }
582 
583   if (opt_port)
584   {
585     dynstr_append(&path_buf, "\nport = ");
586     dynstr_append(&path_buf, opt_port);
587   }
588 
589   dynstr_append(&path_buf, "\n");
590 
591   /* Warn if login path already exists */
592   if (opt_warn && ((locate_login_path (&file_buf, opt_login_path))
593                    != NULL))
594   {
595     int choice;
596     printf ("WARNING : \'%s\' path already exists and will be "
597             "overwritten. \n Continue? (Press y|Y for Yes, any "
598             "other key for No) : ",
599             opt_login_path);
600     choice= getchar();
601 
602     if (choice != (int) 'y' && choice != (int) 'Y')
603       goto done;                                /* skip */
604   }
605 
606   /* Remove the login path. */
607   remove_login_path(&file_buf, opt_login_path);
608 
609   /* Append the new login path to the file buffer. */
610   dynstr_append(&file_buf, path_buf.str);
611 
612   if (encrypt_and_write_file(&file_buf) == -1)
613     goto error;
614 
615 done:
616   dynstr_free(&file_buf);
617   dynstr_free(&path_buf);
618   DBUG_RETURN(0);
619 
620 error:
621   dynstr_free(&file_buf);
622   dynstr_free(&path_buf);
623   DBUG_RETURN(-1);
624 }
625 
remove_command(void)626 static int remove_command(void) {
627   DBUG_ENTER("remove_command");
628 
629   DYNAMIC_STRING file_buf, path_buf;
630 
631   init_dynamic_string(&path_buf, "", MY_LINE_MAX, MY_LINE_MAX);
632   init_dynamic_string(&file_buf, "", file_size, 3 * MY_LINE_MAX);
633 
634   if (file_size)
635   {
636     if (read_and_decrypt_file(&file_buf) == -1)
637       goto error;
638   }
639   else
640     goto done;                                  /* Nothing to remove, skip.. */
641 
642   /* Warn if no login path is specified. */
643   if (opt_warn &&
644       ((locate_login_path (&file_buf, opt_login_path)) != NULL) &&
645       (login_path_specified == FALSE)
646       )
647   {
648     int choice;
649     printf ("WARNING : No login path specified, so options from the default "
650             "login path will be removed.\nContinue? (Press y|Y for Yes, "
651             "any other key for No) : ");
652     choice= getchar();
653 
654     if (choice != (int) 'y' && choice != (int) 'Y')
655       goto done;                                /* skip */
656   }
657 
658   remove_options(&file_buf, opt_login_path);
659 
660   if (encrypt_and_write_file(&file_buf) == -1)
661     goto error;
662 
663 done:
664   dynstr_free(&file_buf);
665   dynstr_free(&path_buf);
666   DBUG_RETURN(0);
667 
668 error:
669   dynstr_free(&file_buf);
670   dynstr_free(&path_buf);
671   DBUG_RETURN(-1);
672 }
673 
674 /**
675   Execute 'print' command.
676 
677   @param void
678 
679   @return -1              Error
680            0              Success
681 */
682 
print_command(void)683 static int print_command(void)
684 {
685   DBUG_ENTER("print_command");
686   DYNAMIC_STRING file_buf;
687 
688   init_dynamic_string(&file_buf, "", file_size, 3 * MY_LINE_MAX);
689 
690   if (file_size)
691   {
692     if (read_and_decrypt_file(&file_buf) == -1)
693       goto error;
694   }
695   else
696     goto done;                                  /* Nothing to print, skip..*/
697 
698   print_login_path(&file_buf, opt_login_path);
699 
700 done:
701   dynstr_free(&file_buf);
702   DBUG_RETURN(0);
703 
704 error:
705   dynstr_free(&file_buf);
706   DBUG_RETURN(-1);
707 }
708 
709 
710 /**
711   Create the login file if it does not exist, check
712   and set its permissions and modes.
713 
714   @param void
715 
716   @return  TRUE           Error
717            FALSE          Success
718 */
719 
check_and_create_login_file(void)720 static my_bool check_and_create_login_file(void)
721 {
722   DBUG_ENTER("check_and_create_login_file");
723 
724   MY_STAT stat_info;
725 
726 // This is a hack to make it compile. File permissions are different on Windows.
727 #ifdef _WIN32
728 #define S_IRUSR  00400
729 #define S_IWUSR  00200
730 #define S_IRWXU  00700
731 #define S_IRWXG  00070
732 #define S_IRWXO  00007
733 #endif
734 
735   const int access_flag= (O_RDWR | O_BINARY);
736   const ushort create_mode= (S_IRUSR | S_IWUSR );
737 
738   /* Get the login file name. */
739   if (! my_default_get_login_file(my_login_file, sizeof(my_login_file)))
740   {
741     my_perror("failed to set login file name");
742     goto error;
743   }
744 
745   /*
746     NOTE : MYSQL_TEST_LOGIN_FILE env must be a full path,
747     where the directory structure must exist. However the
748     login file will be created if it does not exist.
749   */
750 #ifdef _WIN32
751   if (! (getenv("MYSQL_TEST_LOGIN_FILE")))
752   {
753     /* Check if 'MySQL' directory is in place. */
754     MY_STAT stat_info_dir;
755     char login_dir[FN_REFLEN];
756     size_t size;
757 
758     dirname_part(login_dir, my_login_file, &size);
759     /* Remove the trailing '\' */
760     if (is_directory_separator(login_dir[-- size]))
761       login_dir[size]= 0;
762 
763     /* Now check if directory exists? */
764     if ( my_stat(login_dir, &stat_info_dir, MYF(0)))
765     {
766       verbose_msg("%s directory exists.\n", login_dir);
767     }
768     else
769     {
770       /* Create the login directory. */
771       verbose_msg("%s directory doesn't exist, creating it.\n", login_dir);
772       if (my_mkdir(login_dir, 0, MYF(0)))
773       {
774         my_perror("failed to create the directory");
775         goto error;
776       }
777     }
778   }
779 #endif
780 
781   /* Check for login file's existence and permissions (0600). */
782   if (my_stat(my_login_file, &stat_info, MYF(0)))
783   {
784     verbose_msg("File exists.\n");
785 
786     file_size= (size_t) stat_info.st_size;
787 
788 #ifdef _WIN32
789     if (1)
790 #else
791     if (!(stat_info.st_mode & (S_IXUSR | S_IRWXG | S_IRWXO)))
792 #endif
793     {
794       verbose_msg("File has the required permission.\nOpening the file.\n");
795       if ((g_fd= my_open(my_login_file, access_flag, MYF(MY_WME))) == -1)
796       {
797         my_perror("couldn't open the file");
798         goto error;
799       }
800     }
801     else
802     {
803       verbose_msg("File does not have the required permission.\n");
804       printf ("WARNING : Login file does not have the required"
805               " file permissions.\nPlease set the mode to 600 &"
806               " run the command again.\n");
807       goto error;
808     }
809   }
810   else
811   {
812     verbose_msg("File does not exist.\nCreating login file.\n");
813     if ((g_fd= my_create(my_login_file, create_mode, access_flag,
814                        MYF(MY_WME))) == -1)
815     {
816       my_perror("couldn't create the login file");
817       goto error;
818     }
819     else
820     {
821       verbose_msg("Login file created.\n");
822       my_close(g_fd, MYF(MY_WME));
823       verbose_msg("Opening the file.\n");
824 
825       if((g_fd= my_open(my_login_file, access_flag, MYF(MY_WME))) == -1)
826       {
827         my_perror("couldn't open the file");
828         goto error;
829       }
830       file_size= 0;
831     }
832   }
833 
834   if (file_size == 0)
835   {
836     generate_login_key();
837     if(add_header() == -1)
838       goto error;
839   }
840   else
841   {
842     if (read_login_key() == -1)
843       goto error;
844   }
845 
846   DBUG_RETURN(FALSE);
847 
848 error:
849   DBUG_RETURN(TRUE);
850 }
851 
852 
853 /**
854   Print options under the specified login path. If '--all'
855   option is used, print all the optins stored in the login
856   file.
857 
858   @param file_buf  [in]   Buffer storing the unscrambled login
859                           file contents.
860   @param path_name [in]   Path name.
861 
862   @return                 void
863 
864 */
865 
print_login_path(DYNAMIC_STRING * file_buf,const char * path_name)866 static void print_login_path(DYNAMIC_STRING *file_buf, const char *path_name)
867 {
868   DBUG_ENTER("print_login_path");
869 
870   char *start= NULL, *end= NULL, temp= '\0';
871 
872   if (file_buf->length == 0)
873     goto done;                                  /* Nothing to print. */
874 
875   if (opt_all)
876   {
877     start= file_buf->str;
878     end= file_buf->str + file_buf->length;
879   }
880   else
881   {
882     start= locate_login_path(file_buf, path_name);
883     if (! start)
884       /* login path not found, skip..*/
885       goto done;
886 
887     end= strstr(start, "\n[");
888   }
889 
890   if (end)
891   {
892     temp= *end;
893     *end= '\0';
894   }
895 
896   mask_password_and_print(start);
897 
898   if (temp != '\0')
899     *end= temp;
900 
901 done:
902   DBUG_VOID_RETURN;
903 }
904 
905 
906 /**
907   Print the specified buffer by masking the actual
908   password string.
909 
910   @param buf [in]         Buffer to be printed.
911 
912   @raturn                 void
913 */
914 
mask_password_and_print(char * buf)915 static void mask_password_and_print(char *buf)
916 {
917   DBUG_ENTER("mask_password_and_print");
918   const char *password_str= "\npassword = ", *mask = "*****";
919   char *next= NULL;
920 
921   while ((next= strstr(buf, password_str)) != NULL)
922   {
923     while ( *buf != 0 && buf != next)
924       putc( *(buf ++), stdout);
925     printf("%s", password_str);
926     printf("%s\n", mask);
927     if (*buf == '\n')                           /* Move past \n' */
928       buf ++;
929 
930     /* Ignore the password. */
931     while( *buf && *(buf ++) != '\n')
932     {}
933 
934     if ( !opt_all)
935       break;
936   }
937 
938   /* Now print the rest of the buffer. */
939   while ( *buf) putc( *(buf ++), stdout);
940   // And a new line.. if required.
941   if (* (buf - 1) != '\n')
942     putc('\n', stdout);
943 
944   DBUG_VOID_RETURN;
945 }
946 
947 
948 /**
949   Remove multiple options from a login path.
950 */
remove_options(DYNAMIC_STRING * file_buf,const char * path_name)951 static void remove_options(DYNAMIC_STRING *file_buf, const char *path_name)
952 {
953   /* If nope of the options are specified remove the entire path. */
954   if (!opt_remove_host && !opt_remove_pass && !opt_remove_user
955       && !opt_remove_socket && !opt_remove_port)
956   {
957     remove_login_path(file_buf, path_name);
958     return;
959   }
960 
961   if (opt_remove_user)
962     remove_option(file_buf, path_name, "user");
963 
964   if (opt_remove_pass)
965     remove_option(file_buf, path_name, "password");
966 
967   if (opt_remove_host)
968     remove_option(file_buf, path_name, "host");
969 
970   if (opt_remove_socket)
971     remove_option(file_buf, path_name, "socket");
972 
973   if (opt_remove_port)
974     remove_option(file_buf, path_name, "port");
975 }
976 
977 
978 /**
979   Remove an option from a login path.
980 */
remove_option(DYNAMIC_STRING * file_buf,const char * path_name,const char * option_name)981 static void remove_option(DYNAMIC_STRING *file_buf, const char *path_name,
982                           const char* option_name)
983 {
984   DBUG_ENTER("remove_option");
985 
986   char *start= NULL, *end= NULL;
987   char *search_str;
988   size_t search_len, shift_len;
989   bool option_found= FALSE;
990 
991   search_str= (char *) my_malloc(PSI_NOT_INSTRUMENTED,
992                                  (uint) strlen(option_name) + 2, MYF(MY_WME));
993   sprintf(search_str, "\n%s", option_name);
994 
995   if ((start= locate_login_path(file_buf, path_name)) == NULL)
996     /* login path was not found, skip.. */
997     goto done;
998 
999   end= strstr(start, "\n[");                    /* Next path. */
1000 
1001   if (end)
1002     search_len= end - start;
1003   else
1004     search_len= file_buf->length - (start - file_buf->str);
1005 
1006   while(search_len > 1)
1007   {
1008     if (!strncmp(start, search_str, strlen(search_str)))
1009     {
1010       /* Option found. */
1011       end= start;
1012       while(*(++ end) != '\n')
1013       {}
1014       option_found= TRUE;
1015       break;
1016     }
1017     else
1018     {
1019       /* Move to next line. */
1020       while( (-- search_len > 1) && (*(++ start) != '\n'))
1021       {}
1022     }
1023   }
1024 
1025   if (option_found)
1026   {
1027     shift_len= file_buf->length - (end - file_buf->str);
1028 
1029     file_buf->length -= (end - start);
1030 
1031     while(shift_len --)
1032       *(start ++)= *(end ++);
1033     *start= '\0';
1034   }
1035 
1036 done:
1037   my_free(search_str);
1038   DBUG_VOID_RETURN;
1039 }
1040 
1041 
1042 /**
1043   Remove the specified login path from the login file.
1044 
1045   @param file_buf  [in]   Buffer storing the unscrambled login
1046                           file contents.
1047   @param path_name [in]   Path name.
1048 
1049   @return                 void
1050 
1051 */
1052 
remove_login_path(DYNAMIC_STRING * file_buf,const char * path_name)1053 static void remove_login_path(DYNAMIC_STRING *file_buf, const char *path_name)
1054 {
1055   DBUG_ENTER("remove_login_path");
1056 
1057   char *start=NULL, *end= NULL;
1058   int to_move, len, diff;
1059 
1060   if((start= locate_login_path(file_buf, path_name)) == NULL)
1061     /* login path was not found, skip.. */
1062     goto done;
1063 
1064   end= strstr(start, "\n[");
1065 
1066   if (end)
1067   {
1068     end ++;                                     /* Move past '\n' */
1069     len= ((diff= (start - end)) > 0) ? diff : - diff;
1070     to_move= file_buf->length - (end - file_buf->str) ;
1071   }
1072   else
1073   {
1074     *start= '\0';
1075     file_buf->length= ((diff= (file_buf->str - start)) > 0) ? diff : - diff;
1076     goto done;
1077   }
1078 
1079   while(to_move --)
1080     *(start ++)= *(end ++);
1081 
1082   *start= '\0';
1083   file_buf->length -= len;
1084 
1085 done:
1086   DBUG_VOID_RETURN;
1087 }
1088 
1089 /**
1090   Remove all the contents from the login file.
1091 
1092   @param gen_key [in]     Flag to control the generation of
1093                           a new key.
1094 
1095   @return -1              Error
1096            0              Success
1097 */
1098 
reset_login_file(bool gen_key)1099 static int reset_login_file(bool gen_key)
1100 {
1101   DBUG_ENTER("reset_login_file");
1102 
1103   if (my_chsize(g_fd, 0, 0, MYF(MY_WME)))
1104   {
1105     verbose_msg("Error while truncating the file.\n");
1106     goto error;
1107   }
1108 
1109   /* Seek to the beginning of the file. */
1110   if (my_seek(g_fd, 0L, SEEK_SET, MYF(MY_WME) == MY_FILEPOS_ERROR))
1111     goto error;                                 /* Error. */
1112 
1113   if (gen_key)
1114     generate_login_key();                       /* Generate a new key. */
1115 
1116   if (add_header() == -1)
1117     goto error;
1118 
1119   DBUG_RETURN(0);
1120 
1121 error:
1122   DBUG_RETURN(0);
1123 }
1124 
1125 
1126 /**
1127   Find the specified login path in the login file buffer
1128   and return the starting address.
1129 
1130   @param file_buf  [in]   Buffer storing the unscrambled login
1131                           file contents.
1132   @param path_name [in]   Path name.
1133 
1134   @return                 If found, the starting address of the
1135                           login path, NULL otherwise.
1136 */
1137 
locate_login_path(DYNAMIC_STRING * file_buf,const char * path_name)1138 static char* locate_login_path(DYNAMIC_STRING *file_buf, const char *path_name)
1139 {
1140   DBUG_ENTER("locate_login_path");
1141 
1142   char *addr= NULL;
1143   DYNAMIC_STRING dy_path_name;
1144 
1145   init_dynamic_string(&dy_path_name, "", 512, 512);
1146 
1147   dynstr_append(&dy_path_name, "\n[");
1148   dynstr_append(&dy_path_name, path_name);
1149   dynstr_append(&dy_path_name, "]");
1150 
1151   /* First check if it is the very first login path. */
1152   if (file_buf->str == strstr(file_buf->str, dy_path_name.str + 1))
1153     addr= file_buf->str;
1154   /* If not, scan through the file. */
1155   else
1156   {
1157     addr= strstr(file_buf->str, dy_path_name.str);
1158     if (addr)
1159       addr ++;                                  /* Move past '\n' */
1160   }
1161 
1162   dynstr_free(&dy_path_name);
1163   DBUG_RETURN(addr);
1164 }
1165 
1166 
1167 /**
1168   Encrypt the file buffer and write it to the login file.
1169 
1170   @param file_buf  [in]   Buffer storing the unscrambled login
1171                           file contents.
1172 
1173   @return -1 Error
1174            0 Success
1175 
1176   @note The contents of the file buffer are encrypted
1177         on a line-by-line basis with each line having
1178         the following format :
1179         [<first 4 bytes store cipher-length>|<Next cipher-length
1180         bytes store actual cipher>]
1181 */
1182 
encrypt_and_write_file(DYNAMIC_STRING * file_buf)1183 static int encrypt_and_write_file(DYNAMIC_STRING *file_buf)
1184 {
1185   DBUG_ENTER("encrypt_and_write_file");
1186 
1187   my_bool done= FALSE;
1188   char cipher[MY_LINE_MAX], *tmp= NULL;
1189   uint bytes_read=0, len= 0;
1190   int enc_len= 0;                               // Can be negative.
1191 
1192   if (reset_login_file(0) == -1)
1193     goto error;
1194 
1195   /* Move past key first. */
1196   if (my_seek(g_fd, MY_LOGIN_HEADER_LEN, SEEK_SET, MYF(MY_WME))
1197       != (MY_LOGIN_HEADER_LEN))
1198     goto error;                                 /* Error while seeking. */
1199 
1200 
1201   tmp= &file_buf->str[bytes_read];
1202 
1203   while(! done)
1204   {
1205     len= 0;
1206 
1207     while(*tmp++ != '\n')
1208       if (len < (file_buf->length - bytes_read))
1209         len ++;
1210       else
1211       {
1212         done= TRUE;
1213         break;
1214       }
1215 
1216     if (done)
1217       break;
1218 
1219     if ((enc_len= my_aes_get_size(len + 1, my_aes_128_ecb)) >
1220         (MY_LINE_MAX - (int)MAX_CIPHER_STORE_LEN))
1221     {
1222       my_perror("A parameter to mysql_config_editor exceeds the maximum "
1223                 "accepted length. Please review the data you've supplied "
1224                 "and try to shorten them permissible length.\n");
1225       goto error;
1226     }
1227 
1228     if (encrypt_buffer(&file_buf->str[bytes_read], ++len,
1229                        cipher + MAX_CIPHER_STORE_LEN, enc_len) < 0)
1230       goto error;
1231 
1232     bytes_read += len;
1233 
1234     /* Store cipher length first. */
1235     int4store(cipher, enc_len);
1236 
1237     if ((my_write(g_fd, (const uchar *)cipher, enc_len + MAX_CIPHER_STORE_LEN,
1238                   MYF(MY_WME))) != (enc_len + MAX_CIPHER_STORE_LEN))
1239       goto error;
1240   }
1241 
1242   verbose_msg("Successfully written encrypted data to the login file.\n");
1243 
1244   /* Update file_size */
1245   file_size= bytes_read;
1246 
1247   DBUG_RETURN(0);
1248 
1249 error:
1250   my_perror("couldn't encrypt the file");
1251   DBUG_RETURN(-1);
1252 }
1253 
1254 
1255 /**
1256   Read the login file, unscramble its contents and store
1257   them into the file buffer.
1258 
1259   @param file_buf  [in]   Buffer for storing the unscrambled login
1260                           file contents.
1261 
1262   @return -1 Error
1263            0 Success
1264 */
1265 
read_and_decrypt_file(DYNAMIC_STRING * file_buf)1266 static int read_and_decrypt_file(DYNAMIC_STRING *file_buf)
1267 {
1268   DBUG_ENTER("read_and_decrypt_file");
1269 
1270   char cipher[MY_LINE_MAX], plain[MY_LINE_MAX];
1271   uchar len_buf[MAX_CIPHER_STORE_LEN];
1272   int cipher_len= 0, dec_len= 0;
1273 
1274   /* Move past key first. */
1275   if (my_seek(g_fd, MY_LOGIN_HEADER_LEN, SEEK_SET, MYF(MY_WME))
1276       != (MY_LOGIN_HEADER_LEN))
1277     goto error;                                 /* Error while seeking. */
1278 
1279   /* First read the length of the cipher. */
1280   while (my_read(g_fd, len_buf, MAX_CIPHER_STORE_LEN,
1281                  MYF(MY_WME)) == MAX_CIPHER_STORE_LEN)
1282   {
1283     cipher_len= sint4korr(len_buf);
1284 
1285     if (cipher_len > MY_LINE_MAX)
1286       goto error;
1287 
1288     /* Now read 'cipher_len' bytes from the file. */
1289     if ((int) my_read(g_fd, (uchar *) cipher, cipher_len, MYF(MY_WME)) == cipher_len)
1290     {
1291       if ((dec_len= decrypt_buffer(cipher, cipher_len, plain)) < 0)
1292         goto error;
1293 
1294       plain[dec_len]= 0;
1295       dynstr_append(file_buf, plain);
1296     }
1297   }
1298 
1299   verbose_msg("Successfully decrypted the login file.\n");
1300   DBUG_RETURN(0);
1301 
1302 error:
1303   my_perror("couldn't decrypt the file");
1304   DBUG_RETURN(-1);
1305 }
1306 
1307 
1308 /**
1309   Encrypt the given plain text.
1310 
1311   @param plain     [in]   Plain text to be encrypted.
1312   @param plain_len [in]   Length of the plain text.
1313   @param cipher    [out]  Encrypted cipher text.
1314 
1315   @return                 -1 if error encountered,
1316                           length encrypted, otherwise.
1317 */
1318 
encrypt_buffer(const char * plain,int plain_len,char cipher[],const int aes_len)1319 static int encrypt_buffer(const char *plain, int plain_len, char cipher[], const int aes_len)
1320 {
1321   DBUG_ENTER("encrypt_buffer");
1322 
1323   if (my_aes_encrypt((const unsigned char *) plain, plain_len,
1324                      (unsigned char *) cipher,
1325                      (const unsigned char *) my_key, LOGIN_KEY_LEN,
1326                      my_aes_128_ecb, NULL) == aes_len)
1327     DBUG_RETURN(aes_len);
1328 
1329   verbose_msg("Error! Couldn't encrypt the buffer.\n");
1330   DBUG_RETURN(-1);                              /* Error */
1331 }
1332 
1333 
1334 /**
1335   Decrypt the given cipher text.
1336 
1337   @param cipher     [in]  Cipher text to be decrypted.
1338   @param cipher_len [in]  Length of the cipher text.
1339   @param plain      [out] Decrypted plain text.
1340 
1341   @return                 -1 if error encountered,
1342                           length decrypted, otherwise.
1343 */
1344 
decrypt_buffer(const char * cipher,int cipher_len,char plain[])1345 static int decrypt_buffer(const char *cipher, int cipher_len, char plain[])
1346 {
1347   DBUG_ENTER("decrypt_buffer");
1348   int aes_length;
1349 
1350   if ((aes_length= my_aes_decrypt((const unsigned char *) cipher, cipher_len,
1351                                   (unsigned char *) plain,
1352                                   (const unsigned char *) my_key,
1353                                   LOGIN_KEY_LEN,
1354                                   my_aes_128_ecb, NULL)) > 0)
1355     DBUG_RETURN(aes_length);
1356 
1357   verbose_msg("Error! Couldn't decrypt the buffer.\n");
1358   DBUG_RETURN(-1);                              /* Error */
1359 }
1360 
1361 
1362 /**
1363   Add unused bytes alongwith the to the login key
1364   to the login file.
1365 
1366   @return                 -1 if error encountered,
1367                           length written, otherwise.
1368 */
1369 
add_header(void)1370 static int add_header(void)
1371 {
1372   DBUG_ENTER("add_header");
1373 
1374   /* Reserved for future use. */
1375   const char unused[]= {'\0','\0','\0','\0'};
1376 
1377   /* Write 'unused' bytes first. */
1378   if ((my_write(g_fd, (const uchar *) unused, 4, MYF(MY_WME))) != 4)
1379     goto error;
1380 
1381   /* Write the login key. */
1382   if ((my_write(g_fd, (const uchar *)my_key, LOGIN_KEY_LEN, MYF(MY_WME)))
1383       != LOGIN_KEY_LEN)
1384     goto error;
1385 
1386   verbose_msg("Key successfully written to the file.\n");
1387   DBUG_RETURN(MY_LOGIN_HEADER_LEN);
1388 
1389 error:
1390   my_perror("file write operation failed");
1391   DBUG_RETURN(-1);
1392 }
1393 
1394 
1395 /**
1396   Algorithm to generate key.
1397 */
1398 
generate_login_key()1399 void generate_login_key()
1400 {
1401   DBUG_ENTER("generate_login_key");
1402   struct rand_struct rnd;
1403 
1404   verbose_msg("Generating a new key.\n");
1405   /* Get a sequence of random non-printable ASCII */
1406   for (uint i= 0; i < LOGIN_KEY_LEN; i++)
1407     my_key[i]= (char)((int)(my_rnd_ssl(&rnd) * 100000) % 32);
1408 
1409   DBUG_VOID_RETURN;
1410 }
1411 
1412 /**
1413   Read the stored login key.
1414 
1415   @return -1              Error
1416            0              Success
1417 */
1418 
read_login_key(void)1419 static int read_login_key(void)
1420 {
1421   DBUG_ENTER("read_login_key");
1422 
1423   verbose_msg("Reading the login key.\n");
1424   /* Move past the unused buffer. */
1425   if (my_seek(g_fd, 4, SEEK_SET, MYF(MY_WME)) != 4)
1426     goto error;                                 /* Error while seeking. */
1427 
1428   if (my_read(g_fd, (uchar *)my_key, LOGIN_KEY_LEN, MYF(MY_WME))
1429       != LOGIN_KEY_LEN)
1430     goto error;                                 /* Error while reading. */
1431 
1432   verbose_msg("Login key read successfully.\n");
1433   DBUG_RETURN(0);
1434 
1435 error:
1436   my_perror("file read operation failed");
1437   DBUG_RETURN(-1);
1438 }
1439 
1440 
verbose_msg(const char * fmt,...)1441 static void verbose_msg(const char *fmt, ...)
1442 {
1443   DBUG_ENTER("verbose_msg");
1444   va_list args;
1445 
1446   if (!opt_verbose)
1447     DBUG_VOID_RETURN;
1448 
1449   va_start(args, fmt);
1450   vfprintf(stderr, fmt, args);
1451   va_end(args);
1452   fflush(stderr);
1453 
1454   DBUG_VOID_RETURN;
1455 }
1456 
my_perror(const char * msg)1457 static void my_perror(const char *msg)
1458 {
1459   char errbuf[MYSYS_STRERROR_SIZE];
1460 
1461   if (errno == 0)
1462     fprintf(stderr, "%s\n", (msg) ? msg : "");
1463   else
1464     fprintf(stderr, "%s : %s\n", (msg) ? msg : "",
1465             my_strerror(errbuf, sizeof(errbuf), errno));
1466   // reset errno
1467   errno= 0;
1468 }
1469 
usage_command(int command)1470 static void usage_command(int command)
1471 {
1472   print_version();
1473   puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2012"));
1474   puts("MySQL Configuration Utility.");
1475   printf("\nDescription: %s\n", command_data[command].description);
1476   printf("Usage: %s [program options] [%s [command options]]\n",
1477          my_progname, command_data[command].name);
1478   my_print_help(command_data[command].options);
1479   my_print_variables(command_data[command].options);
1480 }
1481 
1482 
usage_program(void)1483 static void usage_program(void)
1484 {
1485   print_version();
1486   puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2012"));
1487   puts("MySQL Configuration Utility.");
1488   printf("Usage: %s [program options] [command [command options]]\n",
1489          my_progname);
1490   my_print_help(my_program_long_options);
1491   my_print_variables(my_program_long_options);
1492   puts("\nWhere command can be any one of the following :\n\
1493        set [command options]     Sets user name/password/host name/socket/port\n\
1494                                  for a given login path (section).\n\
1495        remove [command options]  Remove a login path from the login file.\n\
1496        print [command options]   Print all the options for a specified\n\
1497                                  login path.\n\
1498        reset [command options]   Deletes the contents of the login file.\n\
1499        help                      Display this usage/help information.\n");
1500 }
1501 
1502 
print_version(void)1503 static void print_version(void) {
1504   printf ("%s Ver %s Distrib %s, for %s on %s\n", my_progname,
1505           MYSQL_CONFIG_EDITOR_VERSION, MYSQL_SERVER_VERSION,
1506           SYSTEM_TYPE, MACHINE_TYPE);
1507 }
1508 
1509