/* $Id: main.c 41 2006-02-04 12:41:09Z dewitge $ MySQL Activity Report Lightweight process that monitors some relevant MySQL parameters Copyright 2004 Gert Dewit */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef CONFIG_H #define CONFIG_H 1 #include "config.h" #endif /* Need global option for MySQL >= 5.x */ #if defined (MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 50000 #define VARIABLESFMTSTR "show global variables like '%s'" #define STATUSFMTSTR "show global status like '%s'" #else #define VARIABLESFMTSTR "show variables like '%s'" #define STATUSFMTSTR "show status like '%s'" #endif #define DEFDATADIR "/var/lib/mysqlard" #define DEFPIDFILE "/var/run/mysqlard.pid" #define DEFRRDKEY "key_cache.rrd" #define DEFRRDCON "connections.rrd" #define DEFRRDTAB "table_cache.rrd" #define DEFRRDQUERY "queries.rrd" #define DEFRRDSLAVE "slave.rrd" unsigned long long ar_get(MYSQL *mysql, char *varname, int var); unsigned long long ar_get_var(MYSQL *mysql, char *varname); unsigned long long ar_get_param(MYSQL *mysql, char *varname); unsigned long long ar_get_sl_param(MYSQL *mysql, char *varname); int update_rrdfile(char *filename, char *cmd); void open_log(void); void do_log(int level, char *fmt, ...); void bye(int exitval); void usage(void); int verbose = 0; int foreground = 0; const char *pid_file = NULL; int main(int argc, char *argv[]) { int c; MYSQL mysql; time_t now; pid_t pid; FILE *pidfile; char conrrdpath[PATH_MAX]; char tabrrdpath[PATH_MAX]; char keyrrdpath[PATH_MAX]; char queryrrdpath[PATH_MAX]; char slaverrdpath[PATH_MAX]; char command[PATH_MAX]; /* options */ int dokey = 1; int dotab = 1; int docon = 1; int doquery = 1; int doslave = 0; unsigned int steptime = 60; const char *host = NULL; const char *user = NULL; const char *passwd = NULL; const char *db = NULL; const char *socket_file = NULL; const char *datadir = NULL; unsigned int port = 0; unsigned long client_flag = 0; /* Relevant variables */ unsigned long long table_cache = 0; unsigned long long max_connections = 0; unsigned long long key_buffer_size = 0; /* Monitored parameters */ unsigned long long open_tables = 0; unsigned long long created_tmp_tables = 0; unsigned long long created_tmp_disk_tables = 0; unsigned long long key_reads = 0; unsigned long long key_read_requests = 0; unsigned long long key_blocks_used = 0; unsigned long long select_full_join = 0; unsigned long long select_range_check = 0; unsigned long long handler_read_key = 0; unsigned long long handler_read_rnd = 0; unsigned long long slow_queries = 0; unsigned long long threads_connected = 0; unsigned long long questions = 0; unsigned long long select = 0; unsigned long long insert = 0; unsigned long long update = 0; unsigned long long delete = 0; unsigned long long read_master_log_pos = 0; unsigned long long exec_master_log_pos = 0; /* handle command line */ while(1) { int option_index = 0; static struct option long_options[] = { {"nokey", 0, 0, 'k'}, {"nocon", 0, 0, 'c'}, {"notab", 0, 0, 't'}, {"noquery", 0, 0, 'q'}, {"slave", 0, 0, 'L'}, {"step", 1, 0, 's'}, {"datadir", 1, 0, 'd'}, {"host", 1, 0, 'h'}, {"password", 1, 0, 'p'}, {"port", 1, 0, 'P'}, {"socket", 1, 0, 'S'}, {"pidfile", 1, 0, 'F'}, {"user", 1, 0, 'u'}, {"verbose", 0, 0, 'v'}, {"foreground", 0, 0, 'f'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "kctqLs::d:h:p:P:S:F:u:vf", long_options, &option_index); if(c == -1) break; switch(c) { case 'k': dokey = 0; break; case 'c': docon = 0; break; case 't': dotab = 0; break; case 'q': doquery = 0; break; case 'L': doslave = 1; break; case 's': steptime = atol(optarg); break; case 'd': datadir = optarg; break; case 'h': host = optarg; break; case 'p': passwd = optarg; break; case 'P': port = atol(optarg); break; case 'F': pid_file = optarg; break; case 'S': socket_file = optarg; break; case 'u': user = optarg; break; case 'v': verbose = 1; break; case 'f': foreground = 1; break; default: usage(); } } if(optind < argc) { usage(); } if(pid_file == NULL) pid_file = strdup(DEFPIDFILE); if(access(pid_file, F_OK) == 0) { fprintf(stderr, "PID file %s exists, exiting ...\n", pid_file); bye(2); } if(datadir == NULL) datadir = strdup(DEFDATADIR); open_log(); if(! opendir(datadir)) { do_log(LOG_CRIT, "data directory \"%s\" does not exist or is inaccessible\n", datadir); bye(3); } if(dotab) { snprintf(tabrrdpath, PATH_MAX, "%s/%s", datadir, DEFRRDTAB); if(access(tabrrdpath, W_OK) < 0) { do_log(LOG_CRIT, "RRD file %s not writeable\n", tabrrdpath); bye(4); } } if(dokey) { snprintf(keyrrdpath, PATH_MAX, "%s/%s", datadir, DEFRRDKEY); if(access(keyrrdpath, W_OK) < 0) { do_log(LOG_CRIT, "RRD file %s not writeable\n", keyrrdpath); bye(5); } } if(docon) { snprintf(conrrdpath, PATH_MAX, "%s/%s", datadir, DEFRRDCON); if(access(conrrdpath, W_OK) < 0) { do_log(LOG_CRIT, "RRD file %s not writeable\n", conrrdpath); bye(6); } } if(doquery) { snprintf(queryrrdpath, PATH_MAX, "%s/%s", datadir, DEFRRDQUERY); if(access(queryrrdpath, W_OK) < 0) { do_log(LOG_CRIT, "RRD file %s not writeable\n", queryrrdpath); bye(7); } } if(doslave) { snprintf(slaverrdpath, PATH_MAX, "%s/%s", datadir, DEFRRDSLAVE); if(access(slaverrdpath, W_OK) < 0) { do_log(LOG_CRIT, "RRD file %s not writeable\n", slaverrdpath); bye(7); // Should change this } } if(! foreground) { if((pid = fork()) < 0) { do_log(LOG_CRIT, "Cannot fork\n"); bye(8); } else { if (pid != 0) exit(0); } pidfile = fopen(pid_file, "w"); if(! pidfile) { do_log(LOG_CRIT, "Cannot create PID file, exiting ...\n"); exit(10); } fprintf(pidfile, "%i\n", getpid()); fclose(pidfile); setsid(); chdir(datadir); umask(0); } do_log(LOG_INFO, "Keytable logging = %d\n", dokey); do_log(LOG_INFO, "Connection logging = %d\n", docon); do_log(LOG_INFO, "Table cache logging = %d\n", dotab); do_log(LOG_INFO, "Slave logging = %d\n", doslave); do_log(LOG_INFO, "Step time = %d\n", steptime); do_log(LOG_INFO, "Data dir = %s\n", datadir); do_log(LOG_INFO, "PID file = %s\n", pid_file); do_log(LOG_INFO, "MySQL host = %s\n", host ? host : (char *) LOCAL_HOST); do_log(LOG_INFO, "MySQL port = %d\n", port ? port : 3306); do_log(LOG_INFO, "MySQL socket = %s\n", socket_file ? socket_file : "none"); do_log(LOG_INFO, "MySQL user = %s\n", user ? user : "current user"); do_log(LOG_DEBUG, "MySQL user password = %s\n", passwd ? passwd : "none"); MY_INIT(argv[0]); mysql_init(&mysql); if (!mysql_real_connect(&mysql, host, user, passwd, db, port, socket_file, client_flag)) { do_log(LOG_EMERG, "connect to server at '%s' failed, error: '%s'", host ? host : (char *) LOCAL_HOST, mysql_error(&mysql)); bye(11); } for(;;) { now = time(NULL); do_log(LOG_DEBUG, "Data at %d", (int) now); /* DEBUG */ /* Table Cache */ if(dotab) { table_cache = ar_get_var(&mysql, "table_cache"); open_tables = ar_get_param(&mysql, "open_tables"); created_tmp_tables = ar_get_param(&mysql, "created_tmp_tables"); created_tmp_disk_tables = ar_get_param(&mysql, "created_tmp_disk_tables"); if(foreground) { printf("update %s %d:%llu:%llu:%llu:%llu\n", tabrrdpath, (int) now, open_tables, table_cache, created_tmp_tables, created_tmp_disk_tables); } snprintf(command, PATH_MAX, "N:%llu:%llu:%llu:%llu", open_tables, table_cache, created_tmp_tables, created_tmp_disk_tables); update_rrdfile(tabrrdpath, command); } /* Key cache */ if(dokey) { key_reads = ar_get_param(&mysql, "key_reads"); key_read_requests = ar_get_param(&mysql, "key_read_requests"); key_buffer_size = ar_get_var(&mysql, "key_buffer_size"); key_blocks_used = ar_get_param(&mysql, "key_blocks_used") * 1024; select_full_join = ar_get_param(&mysql, "select_full_join"); select_range_check = ar_get_param(&mysql, "select_range_check"); handler_read_key = ar_get_param(&mysql, "handler_read_key"); handler_read_rnd = ar_get_param(&mysql, "handler_read_rnd"); slow_queries = ar_get_param(&mysql, "slow_queries"); if(foreground) { printf("update %s %d:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu\n", keyrrdpath, (int) now, key_reads, key_read_requests, key_buffer_size, key_blocks_used, select_full_join, select_range_check, handler_read_key, handler_read_rnd, slow_queries); } snprintf(command, PATH_MAX, "N:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu", key_reads, key_read_requests, key_buffer_size, key_blocks_used, select_full_join, select_range_check, handler_read_key, handler_read_rnd, slow_queries); update_rrdfile(keyrrdpath, command); } /* Connections */ if(docon) { max_connections = ar_get_var(&mysql, "max_connections"); threads_connected = ar_get_param(&mysql, "threads_connected"); if(foreground) { printf("update %s %d:%llu:%llu\n", conrrdpath, (int) now, threads_connected, max_connections); } snprintf(command, PATH_MAX, "N:%llu:%llu", threads_connected, max_connections); update_rrdfile(conrrdpath, command); } /* Queries */ if(doquery) { questions = ar_get_param(&mysql, "questions"); select = ar_get_param(&mysql, "com_select"); insert = ar_get_param(&mysql, "com_insert"); update = ar_get_param(&mysql, "com_update"); delete = ar_get_param(&mysql, "com_delete"); if(foreground) { printf("update %s %d:%llu:%llu:%llu:%llu:%llu\n", queryrrdpath, (int) now, questions, select, insert, update, delete); } snprintf(command, PATH_MAX, "N:%llu:%llu:%llu:%llu:%llu", questions, select, insert, update, delete); update_rrdfile(queryrrdpath, command); } /* Slave */ if(doslave) { read_master_log_pos = ar_get_sl_param(&mysql, "read_master_log_pos"); exec_master_log_pos = ar_get_sl_param(&mysql, "exec_master_log_pos"); if(foreground) { printf("update %s %d:%llu:%llu\n", slaverrdpath, (int) now, read_master_log_pos, exec_master_log_pos); } snprintf(command, PATH_MAX, "N:%llu:%llu", read_master_log_pos, exec_master_log_pos); update_rrdfile(slaverrdpath, command); } sleep(steptime); } mysql_close(&mysql); return 0; } unsigned long long ar_get_var(MYSQL *mysql, char *varname) { return ar_get(mysql, varname, 1); } unsigned long long ar_get_param(MYSQL *mysql, char *varname) { return ar_get(mysql, varname, 0); } unsigned long long ar_get(MYSQL *mysql, char *varname, int var) { unsigned long long retval; MYSQL_RES *res; MYSQL_ROW row; char query[2048]; if(var) { snprintf(query, 2048, VARIABLESFMTSTR, varname); } else { snprintf(query, 2048, STATUSFMTSTR, varname); } if (mysql_query(mysql, query) || !(res=mysql_store_result(mysql))) { do_log(LOG_ERR, "Query \"%s\" failed; error : \"%s\"", query, mysql_error(mysql)); return 0; } row=mysql_fetch_row(res); if(row) { retval = strtoull(row[1], NULL, 10); } mysql_free_result(res); return retval; } unsigned long long ar_get_sl_param(MYSQL *mysql, char *varname) { unsigned long long retval; MYSQL_RES *res; MYSQL_ROW row; char query[100]; unsigned int field_count; unsigned int col; MYSQL_FIELD *fields; snprintf(query, 100, "show slave status"); if (mysql_query(mysql, query) || !(res=mysql_store_result(mysql))) { do_log(LOG_ERR, "Query \"%s\" failed; error : \"%s\"", query, mysql_error(mysql)); return 0; } retval = 0; row = mysql_fetch_row(res); field_count = mysql_num_fields(res); fields = mysql_fetch_fields(res); for( col = 0 ; col < field_count ; col++ ) { if( strcasecmp(varname, fields[col].name) == 0 ) { retval = strtoull(row[col], NULL, 10); // break; } } mysql_free_result(res); return retval; } int update_rrdfile(char *filename, char *cmd) { char **argv; int retval = 0; if(rrd_test_error()) rrd_clear_error(); argv = (char **) malloc(4 * sizeof(char *)); if(! argv) return -2; argv[0] = PACKAGE; argv[1] = strdup("update"); argv[2] = strdup(filename); argv[3] = strdup(cmd); optind = 0; opterr = 0; if(rrd_update(3, &argv[1]) == -1) { fprintf(stderr, "%s\n", rrd_get_error()); retval = -1; } free(argv[1]); free(argv[2]); free(argv[3]); free(argv); return retval; } void open_log(void) { if(! foreground) openlog(PACKAGE_NAME, 0, LOG_DAEMON); } void do_log(int level, char *fmt, ...) { va_list ap; va_start(ap, fmt); if(foreground) { if(verbose || (level == LOG_EMERG) || (level == LOG_ALERT) || (level == LOG_CRIT) || (level == LOG_ERR)) vfprintf(stderr, fmt, ap); } else { vsyslog(level, fmt, ap); } } void bye(int exitval) { if(access(pid_file, F_OK) == 0) { if(unlink(pid_file)) { do_log(LOG_CRIT, "Cannot remove PID file %s\n", pid_file); } } do_log(LOG_CRIT, "Exiting\n"); exit(exitval); } void usage(void) { fprintf(stderr,"%s\nUsage : %s [-k | --nokey] [-c | --nocon] [-t | --notab] \ [-q | --noquery] [-L | --slave] [-s val | --step=val] [-d dir | --datadir=dir] \ [-h hostname | --host=hostname] [-p passwd | --password=passwd] \ [-P port | --port=port] [-S socket | --socket socket] \ [-F file | --pidfile=file] [-u user | --user=user] [-v | --verbose] \ [-f | --foreground]\n\nCheck the manpage for detailed usage information.\n", PACKAGE_STRING, PACKAGE); exit(1); }