mysql-admutils/mysql-dbadm.c

522 lines
13 KiB
C
Raw Normal View History

/*
* @(#) $Header: /tmp/cvs/mysql-admutils/mysql-dbadm.c,v 1.12 2004-11-16 20:56:51 lkarsten Exp $
*
* mysql-dbadm.c
*
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <mysql.h>
#include <assert.h>
#include <stdarg.h>
#include <stdlib.h>
#include <pwd.h>
#include <grp.h>
#include <sys/types.h>
#include <unistd.h>
#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");
2002-03-10 18:43:53 +01:00
printf("Report bugs to drift@stud.ntnu.no\n");
return 0;
}
int
create(MYSQL *pmysql, char *db)
{
// hvis man fors<72>ker <20> dra mysql_create_db() p<> en database som
// allerede finnes, s<> henger bare hele sql-kallet. Vi m<> derfor
// fors<72>ke <20> 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.
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_select_db(pmysql, db)) {
dberror(pmysql, "Database '%s' doesn't exists.", db);
return 0;
}
mysql_select_db(pmysql, "mysql");
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;
// variabler som brukes til <20> 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) { // itererer over alle grupper en person er med i
if (*cp == NULL)
break;
#ifdef DEBUG
2003-04-07 18:21:36 +02:00
printf("cp er %s\n", *cp);
#endif
2003-04-07 18:21:36 +02:00
escaped_user[0] = '\0';
cp_kopi=*cp;
2003-04-07 18:21:36 +02:00
// 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, "\\");
}
2003-04-07 18:21:36 +02:00
escaped_user[strlen(escaped_user) + 1] = '\0';
escaped_user[strlen(escaped_user)] = cp_kopi[i];
} // for
wild = malloc(strlen(escaped_user)+3);
sprintf(wild, "%s\\_%%", escaped_user);
#ifdef DEBUG
2003-04-07 18:21:36 +02:00
printf("dbadm: wildcard: '%s'\n", wild);
#endif
2003-04-07 18:21:36 +02:00
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]);
}
}
2003-04-07 18:21:36 +02:00
free(wild);
cp++;
}
wild = malloc(strlen(p->pw_name) + 3);
2003-04-07 18:21:36 +02:00
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]);
}
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,"
2002-03-06 03:28:27 +01:00
"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 "
2002-03-06 03:28:27 +01:00
"Select Insert Update Delete Create Drop Alter Index\n");
fprintf(f, "# ---------------- "
2002-03-06 03:28:27 +01:00
"------ ------ ------ ------ ------ ---- ----- -----\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);
2002-03-06 03:28:27 +01:00
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,
2002-03-06 03:28:27 +01:00
*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[] */
mkstemp(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);
2002-03-06 03:28:27 +01:00
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, "
2002-03-06 03:28:27 +01:00
"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,
2002-03-06 03:28:27 +01:00
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;
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], "--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 <20> 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 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 {
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[33] = '\0';
if (! (owner(db) || member(db))) {
dberror(NULL, "You are not the owner of '%s'. Skipping.",
db);
continue;
}
switch (command) {
case c_create:
create(&mysql, db);
break;
case c_drop:
drop(&mysql, db);
break;
case c_editperm:
editperm(&mysql, db);
break;
case c_show:
show(&mysql, db);
break;
default:
return dberror(NULL, "This point should never be reached!");
}
} // for
} // else
mysql_reload(&mysql);
mysql_close(&mysql);
return 0;
}