int main (int argc, char **argv) { int c; bool running_suid; void *lockstate; char *scorefile; char *nl; const char *prefix, *user_prefix = NULL; struct stat buf; struct score_entry *scores; struct score_entry newscore; bool reverse = false; ptrdiff_t scorecount, scorealloc; ptrdiff_t max_scores = MAX_SCORES; srand (time (0)); while ((c = getopt (argc, argv, "hrm:d:")) != -1) switch (c) { case 'h': usage (EXIT_SUCCESS); break; case 'd': user_prefix = optarg; break; case 'r': reverse = 1; break; case 'm': { intmax_t m = strtoimax (optarg, 0, 10); if (m < 0) usage (EXIT_FAILURE); max_scores = min (m, MAX_SCORES); } break; default: usage (EXIT_FAILURE); } if (argc - optind != 3) usage (EXIT_FAILURE); running_suid = (getuid () != geteuid ()); prefix = get_prefix (running_suid, user_prefix); scorefile = malloc (strlen (prefix) + strlen (argv[optind]) + 2); if (!scorefile) lose_syserr ("Couldn't allocate score file"); strcpy (scorefile, prefix); strcat (scorefile, "/"); strcat (scorefile, argv[optind]); newscore.score = strtoimax (argv[optind + 1], 0, 10); newscore.data = argv[optind + 2]; if (strlen (newscore.data) > MAX_DATA_LEN) newscore.data[MAX_DATA_LEN] = '\0'; nl = strchr (newscore.data, '\n'); if (nl) *nl = '\0'; newscore.username = get_user_id (); if (! newscore.username) lose_syserr ("Couldn't determine user id"); if (stat (scorefile, &buf) < 0) lose_syserr ("Failed to access scores file"); if (lock_file (scorefile, &lockstate) < 0) lose_syserr ("Failed to lock scores file"); if (read_scores (scorefile, &scores, &scorecount, &scorealloc) < 0) { unlock_file (scorefile, lockstate); lose_syserr ("Failed to read scores file"); } if (push_score (&scores, &scorecount, &scorealloc, &newscore) < 0) { unlock_file (scorefile, lockstate); lose_syserr ("Failed to add score"); } sort_scores (scores, scorecount, reverse); /* Limit the number of scores. If we're using reverse sorting, then also increment the beginning of the array, to skip over the *smallest* scores. Otherwise, just decrementing the number of scores suffices, since the smallest is at the end. */ if (scorecount > max_scores) { if (reverse) scores += scorecount - max_scores; scorecount = max_scores; } if (write_scores (scorefile, scores, scorecount) < 0) { unlock_file (scorefile, lockstate); lose_syserr ("Failed to write scores file"); } if (unlock_file (scorefile, lockstate) < 0) lose_syserr ("Failed to unlock scores file"); exit (EXIT_SUCCESS); }
int main (int argc, char **argv) { int c; bool running_suid, running_sgid; void *lockstate; char *scorefile; char *end, *nl, *user, *data; const char *prefix, *user_prefix = NULL; struct score_entry *scores; struct score_entry newscore; bool reverse = false; ptrdiff_t scorecount, scorealloc; ptrdiff_t max_scores = MAX_SCORES; srand (time (0)); while ((c = getopt (argc, argv, "hrm:d:")) != -1) switch (c) { case 'h': usage (EXIT_SUCCESS); break; case 'd': user_prefix = optarg; break; case 'r': reverse = 1; break; case 'm': { intmax_t m = strtoimax (optarg, &end, 10); if (optarg == end || *end || m < 0) usage (EXIT_FAILURE); max_scores = min (m, MAX_SCORES); } break; default: usage (EXIT_FAILURE); } if (argc - optind != 3) usage (EXIT_FAILURE); running_suid = (getuid () != geteuid ()); running_sgid = (getgid () != getegid ()); if (running_suid && running_sgid) lose ("This program can run either suid or sgid, but not both."); prefix = get_prefix (running_suid || running_sgid, user_prefix); scorefile = malloc (strlen (prefix) + strlen (argv[optind]) + 2); if (!scorefile) lose_syserr ("Couldn't allocate score file"); char *z = stpcpy (scorefile, prefix); *z++ = '/'; strcpy (z, argv[optind]); newscore.score = normalize_integer (argv[optind + 1]); if (! newscore.score) { fprintf (stderr, "%s: Invalid score\n", argv[optind + 1]); return EXIT_FAILURE; } user = get_user_id (); if (! user) lose_syserr ("Couldn't determine user id"); data = argv[optind + 2]; if (strlen (data) > MAX_DATA_LEN) data[MAX_DATA_LEN] = '\0'; nl = strchr (data, '\n'); if (nl) *nl = '\0'; newscore.user_data = malloc (strlen (user) + 1 + strlen (data) + 1); if (! newscore.user_data || sprintf (newscore.user_data, "%s %s", user, data) < 0) lose_syserr ("Memory exhausted"); if (lock_file (scorefile, &lockstate) < 0) lose_syserr ("Failed to lock scores file"); if (read_scores (scorefile, &scores, &scorecount, &scorealloc) < 0) { unlock_file (scorefile, lockstate); lose_syserr ("Failed to read scores file"); } if (push_score (&scores, &scorecount, &scorealloc, &newscore) < 0) { unlock_file (scorefile, lockstate); lose_syserr ("Failed to add score"); } sort_scores (scores, scorecount, reverse); /* Limit the number of scores. If we're using reverse sorting, then also increment the beginning of the array, to skip over the *smallest* scores. Otherwise, just decrementing the number of scores suffices, since the smallest is at the end. */ if (scorecount > max_scores) { if (reverse) scores += scorecount - max_scores; scorecount = max_scores; } if (write_scores (scorefile, running_sgid ? 0664 : 0644, scores, scorecount) < 0) { unlock_file (scorefile, lockstate); lose_syserr ("Failed to write scores file"); } if (unlock_file (scorefile, lockstate) < 0) lose_syserr ("Failed to unlock scores file"); exit (EXIT_SUCCESS); }