/* * @(#) $Header: /tmp/cvs/mysql-admutils/mysql-dbadm.c,v 1.8 2003-04-03 22:30:40 lkarsten Exp $ * * mysql-dbadm.c * */ #include #include #include #include #include #include #include #include #include #include #include #include "mysql-admutils.h" 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) { #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("\n"); printf("Report bugs to drift@stud.ntnu.no\n"); return 0; } int create(MYSQL *pmysql, char *db) { if (mysql_create_db(pmysql, db)) 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]; sprintf(query, "delete from db where db = '%s'", db); if (mysql_query(pmysql, query)) dberror(pmysql, "Failed to delete permissions for database '%s'.", db); if (mysql_drop_db(pmysql, db)) 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; 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) { if (*cp == NULL) break; wild = malloc(strlen(*cp)+3); sprintf(wild, "%s\_%%", *cp); #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); cp++; } wild = malloc(strlen(p->pw_name) + 3); sprintf(wild, "%s_%%", p->pw_name); 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]); } 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; return dblist; } int writeperm(FILE *f, MYSQL *pmysql, const char *db) { char query[1024]; MYSQL_RES *res; int rows, i; MYSQL_ROW row; sprintf(query, "select user,select_priv,insert_priv,update_priv," "delete_priv,create_priv,drop_priv,alter_priv,index_priv from db where db='%s'", db); if (mysql_query(pmysql, query)) return dberror(pmysql, "Query for permissions failed."); res = mysql_store_result(pmysql); rows = mysql_num_rows(res); fprintf(f, "# User " "Select Insert Update Delete Create Drop Alter Index\n"); fprintf(f, "# ---------------- " "------ ------ ------ ------ ------ ---- ----- -----\n"); if (rows == 0) fprintf(f, "# (no permissions currently granted to any users)\n"); else for (i = 0; i < rows; i++) { row = mysql_fetch_row(res); fprintf(f, " %-16s %-7s %-7s %-7s %-7s %-7s %-7s %-7s %s\n", row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]); } return 0; } /* show information about the database */ int show(MYSQL *pmysql, const char *db) { printf("Database '%s':\n", db); writeperm(stdout, pmysql, db); return 0; } #define MAX_GRANTS 1024 int editperm(MYSQL *pmysql, const char *db) { char fn[] = "/tmp/mysql-dbadm.tmp.XXXXXX"; FILE *f; char *editor; char cmd[1024]; /* shell command for editing the file */ char line[1024]; /* buffer to hold one line */ char *cp; /* used to interate through a line */ char *user, *select_priv, *insert_priv, *update_priv, *delete_priv, *create_priv, *drop_priv, *alter_priv, *index_priv; char query[1024]; /* used to build a query */ char *queries[MAX_GRANTS]; /* insert queries */ int lines; /* number of grant lines processed */ int i; /* iterate through lines[] */ mktemp(fn); if (strcmp(fn, "") == 0) return dberror(NULL, "Cannot create a unique temporary file name."); f = fopen(fn, "w"); if (f == NULL) return dberror(NULL, "Failed to open temporary file %s.", fn); writeperm(f, pmysql, db); fclose(f); editor = getenv("EDITOR"); if (!editor) editor = "pico"; /* OK since editor won't be freed */ strcpy(cmd, editor); strcat(cmd, " "); strcat(cmd, fn); if (system(cmd) == -1) { dberror(NULL, "Failed to execute '%s'\n", cmd); perror("system"); 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.", PRIV, lines + 1) 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); #undef STRTOK_WHITESPACE #undef CHECK_PRIV sprintf(query, "insert into db (host, db, user, select_priv, insert_priv, " "update_priv, delete_priv, create_priv, drop_priv, alter_priv, index_priv) values " "('%%', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')", db, user, select_priv, insert_priv, update_priv, delete_priv, create_priv, drop_priv, alter_priv, index_priv); 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. */ sprintf(query, "delete from db where db='%s'", db); 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); } return 0; } int main(int argc, char *argv[]) { int i; enum { c_create, c_drop, c_editperm, c_show } command; MYSQL mysql; char **dblist, **p; 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], "--version") == 0) return version(); if (argc < 2) return wrong_use(NULL); /* 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 lease 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_connect(&mysql, db_server, db_user, db_passwd)) 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) { show(&mysql, *p); free(*p); p++; } free(dblist); } else { /* for each supplied database name, perform the requested action */ for (i = 2; i < argc; i++) { if (! (owner(argv[i]) || member(argv[i]))) { dberror(NULL, "You are not the owner of '%s'. Skipping.", argv[i]); continue; } switch (command) { case c_create: create(&mysql, argv[i]); break; case c_drop: drop(&mysql, argv[i]); break; case c_editperm: editperm(&mysql, argv[i]); break; case c_show: show(&mysql, argv[i]); break; default: return dberror(NULL, "This point should never be reached!"); } } } mysql_reload(&mysql); mysql_close(&mysql); return 0; }