[Tux4kids-commits] r356 - tuxmath/trunk/src
tholy-guest at alioth.debian.org
tholy-guest at alioth.debian.org
Fri Dec 7 15:26:42 UTC 2007
Author: tholy-guest
Date: 2007-12-07 15:26:42 +0000 (Fri, 07 Dec 2007)
New Revision: 356
Added:
tuxmath/trunk/src/tuxmath-admin.c
Log:
Initial checkin of tuxmath-admin. For now, the only operation supported is --createhomedirs. Documentation will follow later.
Added: tuxmath/trunk/src/tuxmath-admin.c
===================================================================
--- tuxmath/trunk/src/tuxmath-admin.c (rev 0)
+++ tuxmath/trunk/src/tuxmath-admin.c 2007-12-07 15:26:42 UTC (rev 356)
@@ -0,0 +1,488 @@
+/*
+ tuxmath-admin.c
+
+ Administer user tuxmath accounts: create accounts, clear gold stars, etc.
+
+ by Tim Holy
+ holy at wustl.edu
+
+ Part of "Tux4Kids" Project
+ http://tux4kids.alioth.debian.org/
+ Subversion repository:
+ https://svn.debian.alioth.org/tux4kids/tuxmath/
+
+
+ December 3, 2007
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+// The next two are for mkdir and umask
+#include <sys/types.h>
+#include <sys/stat.h>
+// The next is needed for opendir
+#include <dirent.h>
+
+#ifndef MACOSX
+//#include "../config.h"
+#endif
+
+#define USER_MENU_ENTRIES "user_menu_entries"
+
+#define PATH_MAX 4096
+#define ADMINVERSION "0.1"
+
+void display_help(void);
+void usage(int err, char * cmd);
+int extract_variable(FILE *fp, const char *varname, char** value);
+
+void display_help(void)
+{
+ printf("\ntuxmath-admin\n"
+ "This program allows you to administer tuxmath, and is particularly\n"
+ "useful for schools and the like that may have many users.\n\n"
+ "Examples:\n"
+ " tuxmath-admin --path /servervolume/tuxmath_users --createhomedirs users.csv\n"
+ " tuxmath-admin --createhomedirs users.csv\n"
+ " Creates a user directory tree in location /servervolume/tuxmath_users,\n"
+ " according to the structure specified in users.csv. See configure.pdf\n"
+ " for details. The second syntax is applicable if you've defined the\n"
+ " homedir path in the global configuration file.\n\n"
+ " tuxmath-admin --confighighscores --level 2\n"
+ " Sets up sharing of high scores at level 2 of the hierarchy (top is\n"
+ " level 1). If you've divided things as School:Grade:Classroom:User, then\n"
+ " this would correspond to sharing high scores among all kids in the same\n"
+ " grade.\n\n"
+ " tuxmath-admin --clearhighscores\n"
+ " Clears high scores for all users in the location specified by the homedir\n"
+ " setting in the global configuration file.\n\n"
+ " tuxmath-admin --path /servervolume/tuxmath_users/2ndgrade --clearhighscores\n"
+ " Clears the high scores for all users inside the 2ndgrade hierarchy.\n\n"
+ " tuxmath-admin --cleargoldstars\n"
+ " Clears the gold stars for all users.\n\n"
+ " tuxmath-admin --path /servervolume/tuxmath_users/1st\\ grade/Mrs.\\ Smith --cleargoldstars\n"
+ " Clears the gold stars for all users in Mrs. Smith's first grade class.\n"
+ );
+}
+
+// Extracts a single variable from a configuration file. Returns 1
+// on success and 0 on failure.
+int extract_variable(FILE *fp, const char *varname, char** value)
+{
+ char buf[PATH_MAX];
+ char *param_begin;
+ char *tmpvalue;
+
+ rewind(fp); // start at the beginning of the file
+
+ // Read in a line at a time:
+ while (fgets (buf, PATH_MAX, fp)) {
+ param_begin = buf;
+ // Skip leading whitespace
+ while (isspace(*param_begin))
+ param_begin++;
+ // Skip comments
+ if ((*param_begin == ';') || (*param_begin == '#'))
+ continue;
+ // Test whether it matches the variable name
+ if (strncmp(param_begin,varname,strlen(varname)) == 0) {
+ // Find the "=" sign
+ tmpvalue = strchr(param_begin+strlen(varname), '=');
+ if (tmpvalue == NULL)
+ continue;
+ // Skip whitespace
+ while (isspace(*tmpvalue))
+ tmpvalue++;
+ // Copy the result
+ *value = strdup(tmpvalue);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void create_homedirs(const char *path,const char *file)
+{
+ FILE *fp,*fpue;
+ char buf[PATH_MAX];
+ char *line_begin;
+ char *line_cur;
+ char *line_cur_end;
+ char *copy_start;
+ char fullpath[PATH_MAX];
+ char **current_dirtree = NULL;
+ int current_depth;
+ int old_depth = -1;
+ int max_depth = 0;
+ int this_line_total_depth;
+ int stop_blanking;
+ int i;
+ int len;
+ mode_t mask;
+
+ fp = fopen(file,"r");
+ if (!fp) {
+ fprintf(stderr,"Error: couldn't open %s for reading.\n",file);
+ exit(EXIT_FAILURE);
+ }
+ mask = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH | S_IXUSR | S_IXGRP | S_IXOTH;
+ umask(0x0); // make dirs read/write for everyone
+ while (fgets (buf, PATH_MAX, fp)) {
+ line_begin = buf;
+ // Skip leading whitespace
+ while (isspace(*line_begin))
+ line_begin++;
+ // Skip comments
+ if ((*line_begin == ';') || (*line_begin == '#'))
+ continue;
+ // Make sure this line isn't blank
+ if (strlen(line_begin) == 0)
+ continue;
+ //printf("Read the line %s\n",line_begin);
+
+ // Count the number of levels by counting the commas + 1
+ this_line_total_depth = 1;
+ line_cur = line_begin;
+ while (!(*line_cur == '\r' || *line_cur == '\n')) {
+ if (*line_cur == ',')
+ this_line_total_depth++;
+ line_cur++;
+ }
+
+ // If this is our first time, set up the tree structure
+ if (max_depth == 0) {
+ max_depth = this_line_total_depth;
+ current_dirtree = (char **) malloc(max_depth * sizeof(char*));
+ if (current_dirtree == NULL) {
+ fprintf(stderr,"Error: couldn't allocate memory for directory tree.\n");
+ exit(EXIT_FAILURE);
+ }
+ for (i = 0; i < max_depth; i++) {
+ current_dirtree[i] = (char *) malloc(PATH_MAX * sizeof(char));
+ if (current_dirtree[i] == NULL){
+ fprintf(stderr,"Error: couldn't allocate memory for directory tree.\n");
+ exit(EXIT_FAILURE);
+ } else
+ *(current_dirtree[i]) = '\0'; // initialize with blank string
+ }
+ }
+ else {
+ // Check that this line doesn't change the size of the directory hierarchy
+ if (this_line_total_depth != max_depth) {
+ fprintf(stderr,"Error: line\n '%s'\ncontains a different number of depths to the hierarchy than the previous setting (%d).\n",buf,max_depth);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ // Parse the directories from back to front. Blank fields at the
+ // end indicate a lack of subdirectories; blank fields at the
+ // beginning indicate that the higher levels of the hierarchy are
+ // not to be changed. So these have to be treated differently.
+ *line_cur = '\0'; // replace linefeed with terminal \0
+ line_cur_end = line_cur;
+ current_depth = max_depth-1;
+ stop_blanking = 0;
+ while (current_depth >= 0) {
+ // Back up to the previous comma
+ // Note that line_cur+1 points to the first "real character" of
+ // the string, so don't be bothered that line_cur could get to be
+ // one less than line_begin.
+ while (line_cur >= line_begin && *line_cur != ',')
+ line_cur--;
+ // Determine whether we have a new directory name
+ if (line_cur+1 < line_cur_end) {
+ // We do, copy it over including the terminal \0
+ copy_start = line_cur+1;
+ if (*copy_start == '\"')
+ copy_start++;
+ if (line_cur_end[-1] == '\"') {
+ line_cur_end--;
+ *line_cur_end = '\0';
+ }
+ memcpy(current_dirtree[current_depth],copy_start,line_cur_end-copy_start+1);
+ stop_blanking = 1; // don't clear blank fields in the future
+ }
+ else {
+ // Blank this particular field, because we don't want old
+ // subdirectories hanging around
+ if (!stop_blanking)
+ *(current_dirtree[current_depth]) = '\0';
+ }
+ current_depth--;
+ if (line_cur >= line_begin)
+ *line_cur = '\0'; // end the processing at the comma
+ line_cur_end = line_cur;
+ }
+
+ // Create the full path
+ strncpy(fullpath, path, PATH_MAX);
+ len = strlen(fullpath);
+ if (fullpath[len-1] != '/' && len+1 < PATH_MAX) {
+ fullpath[len] = '/'; // append a slash, if need be
+ fullpath[len+1] = '\0';
+ }
+ for (i = 0; i < max_depth; i++) {
+ len = strlen(fullpath);
+ strncpy(fullpath+len,current_dirtree[i],PATH_MAX-len);
+ len = strlen(fullpath);
+ if (fullpath[len-1] != '/' && len+1 < PATH_MAX) {
+ fullpath[len] = '/'; // append a slash, if need be
+ fullpath[len+1] = '\0';
+ }
+ }
+
+ // Create the directory
+ if (strlen(fullpath) < PATH_MAX) {
+ if (mkdir(fullpath,mask) < 0) {
+ // There was some kind of error, figure out what happened
+ if (errno == EEXIST) {
+ fprintf(stderr,"Warning: %s already exists, continuing.\n",fullpath);
+ }
+ else if (errno == ENAMETOOLONG) {
+ fprintf(stderr,"Error: the directory name:\n %s\nwas too long.\n",fullpath);
+ exit(EXIT_FAILURE);
+ }
+ else if (errno == ENOENT) {
+ fprintf(stderr,"Error: One of the upper-level directories in:\n %s\ndoesn't exist. Check the syntax of your configuration file.\n",fullpath);
+ exit(EXIT_FAILURE);
+ }
+ else if (errno == ENOSPC) {
+ fprintf(stderr,"Error: the device has no room available.\n");
+ exit(EXIT_FAILURE);
+ }
+ else {
+ // This includes EACCESS and all other errors
+ fprintf(stderr,"Error: couldn't make directory %s:\nDo you have write permission for this location?\nDo you need to be root/administrator?\n",fullpath);
+ error(1,errno,"error");
+ }
+ }
+ else {
+ fsync(stderr);
+ fprintf(stdout,"Creating %s\n",fullpath);
+ fsync(stdout);
+
+ // Append the name to the user_menu_entries file
+ // First we split off the last item in fullpath
+ line_begin = fullpath;
+ len = strlen(line_begin);
+ line_begin[len-1] = '\0'; // replace terminal '/' with \0
+ line_cur = line_begin + len-1;
+ while (line_cur > line_begin && *line_cur != '/')
+ line_cur--;
+ if (line_cur > line_begin) { // as long as not making in the root directory...a bad idea anyway!
+ *line_cur = '\0'; // Split into two strings
+ }
+ else {
+ line_begin = "/";
+ }
+ line_cur++; // line_cur now points to beginning of newest directory
+ strncpy(buf,line_begin,PATH_MAX); // we don't need buf anymore
+ buf[strlen(buf)] = '/'; // append directory separator
+ len = strlen(buf);
+ strncpy(buf+len,USER_MENU_ENTRIES,PATH_MAX-len-strlen(USER_MENU_ENTRIES));
+ // Now do the appending
+ fpue = fopen(buf,"a");
+ if (!fpue) {
+ fprintf(stderr,"Error: can't open file %s for writing.\n",buf);
+ exit(EXIT_FAILURE);
+ }
+ len = fprintf(fpue,"%s\n",line_cur);
+ if (len != strlen(line_cur)+1) {
+ fprintf(stderr,"Error writing %s to file %s.\n",line_cur,buf);
+ error(1,error,"");
+ }
+ fclose(fpue);
+ }
+ }
+ else {
+ // The path name was truncated, don't make a corrupt directory
+ fprintf(stderr,"Error: the directory name:\n %s\nwas too long, quitting.\n",fullpath);
+ exit(EXIT_FAILURE);
+ }
+ //printf("Directory: %s\n",fullpath);
+ }
+
+ // Free memory
+ for (i = 0; i < max_depth; i++)
+ free(current_dirtree[i]);
+ if (current_dirtree != NULL)
+ free(current_dirtree);
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+ FILE *fp;
+ DIR *dir;
+
+ int is_creatinghomedirs = 0;
+ int is_confighighscores = 0;
+ int is_clearinggoldstars = 0;
+ int is_clearinghighscores = 0;
+ char *path = NULL;
+ char *file = NULL;
+ int level = 0;
+ int success;
+
+ if (argc < 2) {
+ display_help();
+ exit(EXIT_FAILURE);
+ }
+
+ // Check global config file for a homedir path (must be uncommented)
+ fp = fopen(DATA_PREFIX "/missions/options", "r");
+ if (fp) {
+ extract_variable(fp,"homedir",&path);
+ fclose(fp);
+ }
+
+ // Parse the command line options
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
+ display_help();
+ exit(EXIT_SUCCESS);
+ }
+ else if (strcmp(argv[i], "--copyright") == 0 ||
+ strcmp(argv[i], "-c") == 0)
+ {
+ printf(
+ "\ntuxmath-admin version " ADMINVERSION ", Copyright (C) 2007 Tim Holy\n"
+ "This program is free software; you can redistribute it and/or\n"
+ "modify it under the terms of the GNU General Public License\n"
+ "as published by the Free Software Foundation. See COPYING.txt\n"
+ "\n"
+ "This program is distributed in the hope that it will be useful,\n"
+ "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
+ "\n");
+ exit(EXIT_SUCCESS);
+ }
+ else if (strcmp(argv[i], "--usage") == 0 ||
+ strcmp(argv[i], "-u") == 0) {
+ usage(0, argv[0]);
+ exit(EXIT_SUCCESS);
+ }
+ else if (strcmp(argv[i], "--path") == 0) {
+ if (i+1 > argc) {
+ fprintf(stderr, "%s option requires an argument (a directory name)\n", argv[i]);
+ usage(1, argv[0]);
+ }
+ else {
+ path = argv[i+1];
+ dir = opendir(path); // determine whether directory exists
+ if (dir == NULL) {
+ fprintf(stderr,"Error: path:\n %s\nis not an existing directory, or could not be read.\n",path);
+ exit(EXIT_FAILURE);
+ }
+ closedir(dir);
+ i++; // increment so further processing skips over the argument
+ }
+ }
+ else if (strcmp(argv[i], "--level") == 0) {
+ if (i+1 > argc) {
+ fprintf(stderr, "%s option requires an argument (a level number)\n", argv[i]);
+ usage(1, argv[0]);
+ }
+ else {
+ success = sscanf(argv[i+1],"%d",&level);
+ if (!success) {
+ fprintf(stderr,"level: %s is not a number\n",argv[i+1]);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ else if (strcmp(argv[i], "--createhomedirs") == 0) {
+ is_creatinghomedirs = 1;
+ if (i+1 > argc) {
+ fprintf(stderr, "%s option requires an argument (a file name)\n", argv[i]);
+ usage(1, argv[0]);
+ }
+ else {
+ file = argv[i+1];
+ fp = fopen(file,"r"); // determine whether the file exists
+ if (fp == NULL) {
+ fprintf(stderr,"createhomedirs: %s is not an existing filename, or could not be read\n",file);
+ exit(EXIT_FAILURE);
+ }
+ fclose(fp); // don't read it yet
+ i++; // increment so further processing skips over the argument
+ }
+ }
+ else if (strcmp(argv[i], "--confighighscores") == 0) {
+ is_confighighscores = 1;
+ }
+ else if (strcmp(argv[i], "--clearinghighscores") == 0) {
+ is_clearinghighscores = 1;
+ }
+ else if (strcmp(argv[i], "--clearinggoldstars") == 0) {
+ is_clearinggoldstars = 1;
+ }
+ else {
+ fprintf(stderr,"Error: option %s not recognized.\n",argv[i]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ // All operations require a valid path, so check that now
+ if (path == NULL) {
+ fprintf(stderr,"Must have a valid path (either with --path or in the global configuration)\n");
+ usage(1, argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ // Create homedirs
+ if (is_creatinghomedirs) {
+ if (file == NULL) {
+ fprintf(stderr,"Must specify a filename when creating homedirs\n");
+ usage(1, argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ create_homedirs(path,file);
+ }
+
+ // Configure high scores
+ if (is_confighighscores) {
+ if (level == 0) {
+ fprintf(stderr,"Must specify a level when configuring highscores\n");
+ usage(1, argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ //config_highscores(path,level);
+ }
+
+ // Clear high scores
+ if (is_clearinghighscores) {
+ //clear_highscores(path);
+ }
+
+ // Clear gold stars
+ if (is_clearinggoldstars) {
+ //clear_goldstars(path);
+ }
+
+ return EXIT_SUCCESS;
+}
+
+
+void usage(int err, char * cmd)
+{
+ FILE * f;
+
+ if (err == 0)
+ f = stdout;
+ else
+ f = stderr;
+
+ fprintf(f,
+ "\nUsage: %s {--help | --usage | --copyright}\n"
+ " %s [--path <directory>] --createhomedirs <file>\n"
+ " %s [--level <levelnum>] --confighighscores\n"
+ " %s [--path <directory>] [--clearhighscores] [--cleargoldstars]\n"
+ "\n", cmd, cmd, cmd, cmd);
+
+ exit (err);
+}
+
More information about the Tux4kids-commits
mailing list