/* * @(#) $Header: /home/stud/admin/cvs/mysql-admutils/mysql-dbadm.c,v 1.20 2007/06/04 08:40:54 geirha Exp $ * * mysql-dbadm.c * */ #include "config.h" #include "mysql-admutils.h" #include #include #include #include #include #include #include #include #include #include char * strchr_whitespace(const char *s) { char *sp, *tab; sp = strchr(s, ' '); tab = strchr(s, '\t'); if (sp == NULL) return tab; if (tab == NULL) return sp; return sp < tab ? sp : tab; /* if both are found, return the first one */ } char * strtok_whitespace(char *s) { static char *cp; char *r; r = s ? s : cp; if (r == NULL) return NULL; cp = strchr_whitespace(r); if (cp == NULL) return r; while ((*cp == ' ') || (*cp == '\t')) *cp++ = '\0'; return r; } int valid_priv(const char *s) { if (s == NULL) return 0; #define ACCEPT(x) if (strcmp(s, x) == 0) return 1 ACCEPT("Y"); ACCEPT("N"); ACCEPT("y"); ACCEPT("n"); #undef ACCEPT return 0; /* not a valid priv */ } int usage() { printf("Usage: %s COMMAND [DATABASE]...\n", program_name); printf("Create, drop og edit permission for the DATABASE(s),\n"); printf("as determined by the COMMAND. Valid COMMANDs:\n"); printf("\n"); printf(" create create the DATABASE(s).\n"); printf(" drop delete the DATABASE(s).\n"); printf(" show give information about the DATABASE(s), or, if\n"); printf(" none are given, all the ones you own.\n"); printf(" editperm change permissions for the DATABASE(s). Your\n"); printf(" favorite editor will be started, allowing you\n"); printf(" to make changes to the permission table.\n"); printf(" Run '%s --help-editperm' for more\n" " information.\n",program_name); printf("\n"); printf("Report bugs to orakel@ntnu.no\n"); return 0; } int usage_editperm() { printf("Usage: %s editperm [DATABASE]\n", program_name); printf("Edit permissions for the DATABASE. Running this command will\n"); printf("spawn the editor stored in the $EDITOR environment variable.\n"); printf("(pico will be used if the variable is unset)\n"); printf("\n"); printf("The file should contain one line per user, starting with the\n"); printf("username and followed by ten Y/N-values seperated by whitespace.\n"); printf("Lines starting with # are ignored.\n"); printf("\n"); printf("The Y/N-values corresponds to the following mysql privileges:\n"); printf(" Select - Enables use of SELECT\n"); printf(" Insert - Enables use of INSERT\n"); printf(" Update - Enables use of UPDATE\n"); printf(" Delete - Enables use of DELETE\n"); printf(" Create - Enables use of CREATE TABLE\n"); printf(" Drop - Enables use of DROP TABLE\n"); printf(" Alter - Enables use of ALTER TABLE\n"); printf(" Index - Enables use of CREATE INDEX and DROP INDEX\n"); printf(" Temp - Enables use of CREATE TEMPORARY TABLE\n"); printf(" Lock - Enables use of LOCK TABLE\n"); printf("\n"); printf("Report bugs to orakel@ntnu.no\n"); return 0; } int create(MYSQL *pmysql, char *db) { // hvis man forsøker å dra mysql_create_db() på en database som // allerede finnes, så henger bare hele sql-kallet. Vi må derfor // forsøke å selecte databasen først. Ettersom man alltid er root, // går jo dette vanligvis bra. // finner ut om denne finnes fra før. if (!mysql_select_db(pmysql, db)) { return dberror(pmysql, "Database '%s' already exists.", db); } mysql_select_db(pmysql, "mysql"); // oppretter databasen. char query[1024], *end; end = strmov(query, "CREATE DATABASE `"); end += mysql_real_escape_string(pmysql, end, db, strlen(db)); *end++ = '`'; *end = '\0'; #ifdef DEBUG printf("query: %s\n", query); #endif if (mysql_query(pmysql, query)) return dberror(pmysql, "Cannot create database '%s'.", db); fprintf(stderr, "Database '%s' created.\n", db); return 0; } int drop(MYSQL *pmysql, char *db) { char query[1024], *end; end = strmov(query, "DELETE FROM db WHERE db = '"); end += mysql_real_escape_string(pmysql, end, db, strlen(db)); *end++ = '\''; *end = '\0'; #ifdef DEBUG printf("query: %s\n", query); #endif if (mysql_query(pmysql, query)) dberror(pmysql, "Failed to delete permissions for database '%s'.", db); if (mysql_select_db(pmysql, db)) { dberror(pmysql, "Database '%s' doesn't exists.", db); return 0; } mysql_select_db(pmysql, "mysql"); end = strmov(query, "DROP DATABASE `"); end += mysql_real_escape_string(pmysql, end, db, strlen(db)); *end++ = '`'; *end = '\0'; #ifdef DEBUG printf("query: %s\n", query); #endif if (mysql_query(pmysql, query)) return dberror(pmysql, "Cannot drop database '%s'.", db); fprintf(stderr, "Database '%s' dropped.\n", db); return 0; } /* return a list of the user's databases */ char ** list(MYSQL *pmysql) { char *wild; char **usr_groups, **cp; /* holds all group names this dude is a member of [tlan]*/ MYSQL_RES *res; int rows, numgroups, numgroupdbs; MYSQL_ROW row; char **dblist; int i, counter; struct passwd *p; // variabler som brukes til å escape gruppenavnet til mysqlvennlig format. char escaped_user[64]; char *cp_kopi; p = getpwuid(getuid()); if (!p) { dberror(NULL, "Failed to lookup your UNIX username."); exit(1); } dblist = malloc(2 * sizeof(char*)); /* one for username (if used), rest is done below */ numgroupdbs = 0; counter = 0; usr_groups = get_group_names(&numgroups); cp = usr_groups; while (cp && *cp) { // itererer over alle grupper en person er med i if (*cp == NULL) break; #ifdef DEBUG printf("cp er %s\n", *cp); #endif escaped_user[0] = '\0'; cp_kopi=*cp; // itererer over bokstavene i gruppenavn, og escaper spesialtegn. for(i=0; i<=strlen(cp_kopi); i++) { // hvis % _ , så skriv \ og så tegn if ((cp_kopi[i] == '_') || (cp_kopi[i] == '%')) { strcat(escaped_user, "\\"); } escaped_user[strlen(escaped_user) + 1] = '\0'; escaped_user[strlen(escaped_user)] = cp_kopi[i]; } // for wild = malloc(strlen(escaped_user)+4); sprintf(wild, "%s\\_%%", escaped_user); #ifdef DEBUG printf("dbadm: wildcard: '%s'\n", wild); #endif res = mysql_list_dbs(pmysql, wild); rows = mysql_num_rows(res); if (rows > 0) { numgroupdbs += rows; dblist = realloc(dblist, (numgroupdbs+2) * sizeof(char *)); for (i = 0; i < rows; i++) { if ((row = mysql_fetch_row(res))) { dblist[counter++] = strdup(row[0]); } } } free(wild); mysql_free_result(res); cp++; } wild = malloc(strlen(p->pw_name) + 4); sprintf(wild, "%s\\_%%", p->pw_name); #ifdef DEBUG printf("dbadm: wildcard: '%s'\n", wild); #endif res = mysql_list_dbs(pmysql, wild); rows = mysql_num_rows(res); dblist = realloc(dblist, (numgroupdbs+rows+2) * sizeof(char *)); if (!dblist) { dberror(NULL, "Out of memory.\n"); free(wild); return NULL; } for (i = 0; i < rows; i++) { if ((row = mysql_fetch_row(res))) { dblist[counter++] = strdup(row[0]); } } mysql_free_result(res); res = mysql_list_dbs(pmysql, p->pw_name); rows = mysql_num_rows(res); if (rows == 1) dblist[counter++] = strdup(p->pw_name); dblist[counter] = NULL; mysql_free_result(res); free(wild); for (i=0;inet.fd) != 0) { perror("Failed to close fd"); exit(1); } editor = getenv("EDITOR"); if (!editor) editor = "pico"; /* OK since editor won't be freed */ cmd = malloc(sizeof(char) * strlen(editor) + 7); sprintf(cmd, "%s \"$1\"", editor); /* sh -c '$EDITOR "$1"' sh "$fn" */ execlp("sh", "sh", "-c", cmd, "sh", fn, NULL); perror("Failed to execute editor"); fprintf(stderr, "Make sure the EDITOR environment variable contains" " a valid editor\n"); exit(1); } if (exit_status != 0) return 1; /* parse */ f = fopen(fn, "r"); lines = 0; while (fgets(line, sizeof(line), f)) { cp = strchr(line, '\n'); if (cp) *cp = '\0'; cp = line; while (*cp && ((*cp == ' ') || (*cp == '\t'))) cp++; if (*cp == '\0') continue; if (*cp == '#') continue; if (*cp == '\n') continue; #define STRTOK_WHITESPACE(res, start) \ { if (!(res = strtok_whitespace(start))) continue; } STRTOK_WHITESPACE(user, cp); if (strlen(user) < 1) return dberror(NULL, "Invalid user '%s' in grant line %d.", user, lines + 1); if (strcmp(user, "%") == 0) *user = '\0'; /* ugly, but it works... */ #define CHECK_PRIV(PRIV) \ if (!valid_priv(PRIV)) return dberror(NULL, "Invalid value '%s' " \ "in grant line %d.\nMake sure you fill in a value (Y or N) for " \ "all ten privileges.\nRun '%s --help-editerm' for more " \ "information.\nNo permissions have been set.", PRIV, lines + 1, \ program_name) STRTOK_WHITESPACE(select_priv, NULL); CHECK_PRIV(select_priv); STRTOK_WHITESPACE(insert_priv, NULL); CHECK_PRIV(insert_priv); STRTOK_WHITESPACE(update_priv, NULL); CHECK_PRIV(update_priv); STRTOK_WHITESPACE(delete_priv, NULL); CHECK_PRIV(delete_priv); STRTOK_WHITESPACE(create_priv, NULL); CHECK_PRIV(create_priv); STRTOK_WHITESPACE(drop_priv, NULL); CHECK_PRIV(drop_priv); STRTOK_WHITESPACE(alter_priv, NULL); CHECK_PRIV(alter_priv); STRTOK_WHITESPACE(index_priv, NULL); CHECK_PRIV(index_priv); STRTOK_WHITESPACE(create_tmp_table_priv, NULL); CHECK_PRIV(create_tmp_table_priv); STRTOK_WHITESPACE(lock_tables_priv, NULL); CHECK_PRIV(lock_tables_priv); #undef STRTOK_WHITESPACE #undef CHECK_PRIV end = strmov(query, "INSERT INTO db (" "host,db,user,select_priv,insert_priv," "update_priv,delete_priv,create_priv," "drop_priv,alter_priv,index_priv," "create_tmp_table_priv,lock_tables_priv" ") VALUES ("); end = strmov(end, "'%'"); #define APPEND(VAR) {\ *end++ = ',';\ *end++ = '\'';\ end += mysql_real_escape_string(pmysql, end, VAR, strlen(VAR));\ *end++ = '\'';\ } APPEND(db); APPEND(user); APPEND(select_priv); APPEND(insert_priv); APPEND(update_priv); APPEND(delete_priv); APPEND(create_priv); APPEND(drop_priv); APPEND(alter_priv); APPEND(index_priv); APPEND(create_tmp_table_priv); APPEND(lock_tables_priv); *end++ = ')'; *end = '\0'; #undef APPEND queries[lines] = strdup(query); lines++; if (lines >= MAX_GRANTS) { dberror(NULL, "Warning: Maximum of %d grants reached.\n", MAX_GRANTS); continue; } } /* while fgets ... */ unlink(fn); fclose(f); /* now that we have checked the input for errors, we can safely delete the old grants from the database and insert the new ones. */ end = strmov(query, "DELETE FROM db WHERE db = '"); end += mysql_real_escape_string(pmysql, end, db, strlen(db)); *end++ = '\''; *end = '\0'; #ifdef DEBUG printf("query: %s\n", query); #endif if (mysql_query(pmysql, query)) dberror(pmysql, "Failed to delete old grants for '%s'.", db); for (i = 0; i < lines; i++) { #ifdef DEBUG puts(queries[i]); putchar('\n'); #endif if (mysql_query(pmysql, queries[i])) dberror(pmysql, "Failed to insert grant line %d.", i + 1); free(queries[i]); } fprintf(stderr,"Permissions updated\n"); return 0; } int main(int argc, char *argv[]) { int i; enum { c_create, c_drop, c_editperm, c_show } command; MYSQL mysql; mysql_init(&mysql); char **dblist, **p; char *db; program_name = argv[0]; for (i = 1; i < argc; i++) if (strcmp(argv[i], "--help") == 0) return usage(); for (i = 1; i < argc; i++) if (strcmp(argv[i], "--help-editperm") == 0) return usage_editperm(); for (i = 1; i < argc; i++) if (strcmp(argv[i], "--version") == 0) return version(); if (argc < 2) return wrong_use(NULL); #ifdef DEBUG printf("NB NB NB: denne versjonen av programmet er kompilert med -DDEBUG, og\n"); printf("kan komme til å skrive ut ekstra informasjon. Dette er ikke farlig,\n"); printf("og programmet bør virke som vanlig.\n"); #endif /* check that the supplied command is valid */ if (strcmp(argv[1], "create") == 0) command = c_create; else if (strcmp(argv[1], "drop") == 0) command = c_drop; else if (strcmp(argv[1], "editperm") == 0) command = c_editperm; else if (strcmp(argv[1], "show") == 0) command = c_show; else return wrong_use("unrecognized command"); /* XXX */ /* all other than show requires at least one DATABASE argument. */ if ((command != c_show) && (argc < 3)) return wrong_use(NULL); read_config_file(); /* connect to the database server and select the mysql database */ if (!mysql_real_connect(&mysql, db_server, db_user, db_passwd, db_name, 0, NULL, 0)) return dberror(&mysql, "Cannot connect to database server '%s'.", db_server); if (mysql_select_db(&mysql, db_name)) return dberror(&mysql, "Cannot select database '%s'.", db_name); if ((command == c_show) && (argc == 2)) { dblist = list(&mysql); p = dblist; while (p && *p) { show(&mysql, *p); free(*p); p++; } free(dblist); } else { db = malloc(64); /* for each supplied database name, perform the requested action */ for (i = 2; i < argc; i++) { // HE HE strncpy(db, argv[i], 32); db[32] = '\0'; if (! (owner(db) || member(db))) { if (command == c_create) dberror(NULL,"Unable to create mysql-database '%s'.\n" "A mysql-database must start with either '%s_' or " "'groupname_', where groupname is a unix group you are a " "member of. Type \"groups\" to see which groups you are a " "member of.\n", db, getpwuid(getuid())->pw_name); else dberror(NULL, "You are not in charge of mysql-database: '%s'. Skipping.", db); continue; } switch (command) { case c_create: // We only check newly created databases. Many old ("unclean") databases are still in use. if(name_isclean(db)) { create(&mysql, db); } else { dberror(NULL, "Database name '%s' contains invalid characters.\n" "Only A-Z, a-z, 0-9, _ (underscore) and - (dash) permitted. Skipping.", db); } break; case c_drop: if(name_isclean(db)) { drop(&mysql, db); } else { dberror(NULL, "Database name '%s' contains invalid characters.\n" "Only A-Z, a-z, 0-9, _ (underscore) and - (dash) permitted. Skipping.", db); } break; case c_editperm: if(name_isclean(db)) { editperm(&mysql, db); } else { dberror(NULL, "Database name '%s' contains invalid characters.\n" "Only A-Z, a-z, 0-9, _ (underscore) and - (dash) permitted. Skipping.", db); } break; case c_show: if(name_isclean(db)) { show(&mysql, db); } else { dberror(NULL, "Database name '%s' contains invalid characters.\n" "Only A-Z, a-z, 0-9, _ (underscore) and - (dash) permitted. Skipping.", db); } break; default: return dberror(NULL, "This point should never be reached!"); } } // for free(db); } // else mysql_reload(&mysql); mysql_close(&mysql); return 0; }