diff options
| -rw-r--r-- | lib-src/update-game-score.c | 179 |
1 files changed, 80 insertions, 99 deletions
diff --git a/lib-src/update-game-score.c b/lib-src/update-game-score.c index 0e257e057f5..31d3737d95e 100644 --- a/lib-src/update-game-score.c +++ b/lib-src/update-game-score.c | |||
| @@ -19,8 +19,12 @@ the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |||
| 19 | Boston, MA 02111-1307, USA. */ | 19 | Boston, MA 02111-1307, USA. */ |
| 20 | 20 | ||
| 21 | /* This program is allows a game to securely and atomically update a | 21 | /* This program is allows a game to securely and atomically update a |
| 22 | score file. It should be installed setgid, owned by an appropriate | 22 | score file. It should be installed setuid, owned by an appropriate |
| 23 | group like `games'. | 23 | user like `games'. |
| 24 | |||
| 25 | Alternatively, it can be compiled without HAVE_SHARED_GAME_DIR | ||
| 26 | defined, and in that case it will store scores in the user's home | ||
| 27 | directory (it should NOT be setuid). | ||
| 24 | 28 | ||
| 25 | Created 2002/03/22, by Colin Walters <walters@debian.org> | 29 | Created 2002/03/22, by Colin Walters <walters@debian.org> |
| 26 | */ | 30 | */ |
| @@ -36,15 +40,22 @@ Boston, MA 02111-1307, USA. */ | |||
| 36 | #include <pwd.h> | 40 | #include <pwd.h> |
| 37 | #include <ctype.h> | 41 | #include <ctype.h> |
| 38 | #include <fcntl.h> | 42 | #include <fcntl.h> |
| 43 | #include <stdarg.h> | ||
| 39 | #include <sys/stat.h> | 44 | #include <sys/stat.h> |
| 40 | #include <config.h> | 45 | #include <config.h> |
| 41 | 46 | ||
| 42 | #define MAX_ATTEMPTS 5 | 47 | #define MAX_ATTEMPTS 5 |
| 48 | #define MAX_SCORES 200 | ||
| 49 | #define MAX_DATA_LEN 1024 | ||
| 43 | 50 | ||
| 44 | #ifdef HAVE_SHARED_GAME_DIR | 51 | #ifdef HAVE_SHARED_GAME_DIR |
| 45 | #define SCORE_FILE_PREFIX HAVE_SHARED_GAME_DIR | 52 | #define SCORE_FILE_PREFIX HAVE_SHARED_GAME_DIR |
| 46 | #else | 53 | #else |
| 47 | #define SCORE_FILE_PREFIX "$HOME" | 54 | #define SCORE_FILE_PREFIX "~/.emacs.d/games" |
| 55 | #endif | ||
| 56 | |||
| 57 | #if !defined (__GNUC__) || __GNUC__ < 2 | ||
| 58 | #define __attribute__(x) | ||
| 48 | #endif | 59 | #endif |
| 49 | 60 | ||
| 50 | int | 61 | int |
| @@ -90,7 +101,8 @@ get_user_id(struct passwd *buf) | |||
| 90 | { | 101 | { |
| 91 | int count = 1; | 102 | int count = 1; |
| 92 | int uid = (int) getuid(); | 103 | int uid = (int) getuid(); |
| 93 | while (uid /= 10) | 104 | int tuid = uid; |
| 105 | while (tuid /= 10) | ||
| 94 | count++; | 106 | count++; |
| 95 | name = malloc(count+1); | 107 | name = malloc(count+1); |
| 96 | sprintf(name, "%d", uid); | 108 | sprintf(name, "%d", uid); |
| @@ -107,6 +119,18 @@ get_home_dir(struct passwd *buf) | |||
| 107 | return buf->pw_dir; | 119 | return buf->pw_dir; |
| 108 | } | 120 | } |
| 109 | 121 | ||
| 122 | void lose(const char *msg, ...) | ||
| 123 | __attribute__ ((format (printf,1,0), noreturn)); | ||
| 124 | |||
| 125 | void lose(const char *msg, ...) | ||
| 126 | { | ||
| 127 | va_list ap; | ||
| 128 | va_start(ap, msg); | ||
| 129 | vfprintf(stderr, msg, ap); | ||
| 130 | va_end(ap); | ||
| 131 | exit(1); | ||
| 132 | } | ||
| 133 | |||
| 110 | int | 134 | int |
| 111 | main(int argc, char **argv) | 135 | main(int argc, char **argv) |
| 112 | { | 136 | { |
| @@ -115,7 +139,7 @@ main(int argc, char **argv) | |||
| 115 | char *scorefile, *prefix; | 139 | char *scorefile, *prefix; |
| 116 | struct stat buf; | 140 | struct stat buf; |
| 117 | struct score_entry *scores; | 141 | struct score_entry *scores; |
| 118 | int newscore, scorecount, reverse = 0, max = -1; | 142 | int newscore, scorecount, reverse = 0, max = MAX_SCORES; |
| 119 | char *newdata; | 143 | char *newdata; |
| 120 | struct passwd *passwdbuf; | 144 | struct passwd *passwdbuf; |
| 121 | 145 | ||
| @@ -132,6 +156,8 @@ main(int argc, char **argv) | |||
| 132 | break; | 156 | break; |
| 133 | case 'm': | 157 | case 'm': |
| 134 | max = atoi(optarg); | 158 | max = atoi(optarg); |
| 159 | if (max > MAX_SCORES) | ||
| 160 | max = MAX_SCORES; | ||
| 135 | break; | 161 | break; |
| 136 | default: | 162 | default: |
| 137 | usage(1); | 163 | usage(1); |
| @@ -142,62 +168,65 @@ main(int argc, char **argv) | |||
| 142 | 168 | ||
| 143 | passwdbuf = getpwuid(getuid()); | 169 | passwdbuf = getpwuid(getuid()); |
| 144 | 170 | ||
| 145 | if (!strcmp(SCORE_FILE_PREFIX, "$HOME")) | 171 | if (!strncmp(SCORE_FILE_PREFIX, "~", 1)) |
| 146 | { | 172 | { |
| 147 | prefix = get_home_dir(passwdbuf); | 173 | char *homedir = get_home_dir(passwdbuf); |
| 148 | if (!prefix) | 174 | if (!homedir) |
| 149 | { | 175 | lose("Unable to determine home directory\n"); |
| 150 | fprintf(stderr, "Unable to determine home directory\n"); | 176 | prefix = malloc(strlen(homedir) + strlen(SCORE_FILE_PREFIX) + 1); |
| 151 | exit(1); | 177 | strcpy(prefix, homedir); |
| 152 | } | 178 | /* Skip over the '~'. */ |
| 179 | strcat(prefix, SCORE_FILE_PREFIX+1); | ||
| 153 | } | 180 | } |
| 154 | else | 181 | else |
| 155 | prefix = SCORE_FILE_PREFIX; | 182 | prefix = strdup(SCORE_FILE_PREFIX); |
| 156 | 183 | ||
| 157 | scorefile = malloc(strlen(prefix) + strlen(argv[optind]) + 1); | 184 | if (!prefix) |
| 185 | lose("Couldn't create score file name: %s\n", strerror(errno)); | ||
| 186 | |||
| 187 | scorefile = malloc(strlen(prefix) + strlen(argv[optind]) + 2); | ||
| 158 | if (!scorefile) | 188 | if (!scorefile) |
| 159 | { | 189 | lose("Couldn't create score file name: %s\n", strerror(errno)); |
| 160 | fprintf(stderr, "Couldn't create score file name: %s\n", | 190 | |
| 161 | strerror(errno)); | ||
| 162 | goto fail; | ||
| 163 | } | ||
| 164 | strcpy(scorefile, prefix); | 191 | strcpy(scorefile, prefix); |
| 192 | free(prefix); | ||
| 193 | strcat(scorefile, "/"); | ||
| 165 | strcat(scorefile, argv[optind]); | 194 | strcat(scorefile, argv[optind]); |
| 166 | newscore = atoi(argv[optind+1]); | 195 | newscore = atoi(argv[optind+1]); |
| 167 | newdata = argv[optind+2]; | 196 | newdata = argv[optind+2]; |
| 197 | if (strlen(newdata) > MAX_DATA_LEN) | ||
| 198 | newdata[MAX_DATA_LEN] = '\0'; | ||
| 168 | 199 | ||
| 169 | if (stat(scorefile, &buf) < 0) | 200 | if (stat(scorefile, &buf) < 0) |
| 170 | { | 201 | lose("Failed to access scores file \"%s\": %s\n", scorefile, |
| 171 | fprintf(stderr, "Failed to access scores file \"%s\": %s\n", | 202 | strerror(errno)); |
| 172 | scorefile, strerror(errno)); | ||
| 173 | goto fail; | ||
| 174 | } | ||
| 175 | if (lock_file(scorefile, &lockstate) < 0) | 203 | if (lock_file(scorefile, &lockstate) < 0) |
| 176 | { | 204 | lose("Failed to lock scores file \"%s\": %s\n", |
| 177 | fprintf(stderr, "Failed to lock scores file \"%s\": %s\n", | 205 | scorefile, strerror(errno)); |
| 178 | scorefile, strerror(errno)); | ||
| 179 | goto fail; | ||
| 180 | } | ||
| 181 | if (read_scores(scorefile, &scores, &scorecount) < 0) | 206 | if (read_scores(scorefile, &scores, &scorecount) < 0) |
| 182 | { | 207 | { |
| 183 | fprintf(stderr, "Failed to read scores file \"%s\": %s\n", | 208 | unlock_file(scorefile, lockstate); |
| 184 | scorefile, strerror(errno)); | 209 | lose("Failed to read scores file \"%s\": %s\n", scorefile, |
| 185 | goto fail_unlock; | 210 | strerror(errno)); |
| 186 | } | 211 | } |
| 187 | push_score(&scores, &scorecount, newscore, get_user_id(passwdbuf), newdata); | 212 | push_score(&scores, &scorecount, newscore, get_user_id(passwdbuf), newdata); |
| 213 | /* Limit the number of scores. If we're using reverse sorting, then | ||
| 214 | we should increment the beginning of the array, to skip over the | ||
| 215 | *smallest* scores. Otherwise, we just decrement the number of | ||
| 216 | scores, since the smallest will be at the end. */ | ||
| 217 | if (scorecount > MAX_SCORES) | ||
| 218 | scorecount -= (scorecount - MAX_SCORES); | ||
| 219 | if (reverse) | ||
| 220 | scores += (scorecount - MAX_SCORES); | ||
| 188 | sort_scores(scores, scorecount, reverse); | 221 | sort_scores(scores, scorecount, reverse); |
| 189 | if (write_scores(scorefile, scores, scorecount) < 0) | 222 | if (write_scores(scorefile, scores, scorecount) < 0) |
| 190 | { | 223 | { |
| 191 | fprintf(stderr, "Failed to write scores file \"%s\": %s\n", | 224 | unlock_file(scorefile, lockstate); |
| 192 | scorefile, strerror(errno)); | 225 | lose("Failed to write scores file \"%s\": %s\n", scorefile, |
| 193 | goto fail_unlock; | 226 | strerror(errno)); |
| 194 | } | 227 | } |
| 195 | unlock_file(scorefile, lockstate); | 228 | unlock_file(scorefile, lockstate); |
| 196 | exit(0); | 229 | exit(0); |
| 197 | fail_unlock: | ||
| 198 | unlock_file(scorefile, lockstate); | ||
| 199 | fail: | ||
| 200 | exit(1); | ||
| 201 | } | 230 | } |
| 202 | 231 | ||
| 203 | int | 232 | int |
| @@ -236,11 +265,9 @@ read_score(FILE *f, struct score_entry *score) | |||
| 236 | while ((c = getc(f)) != EOF | 265 | while ((c = getc(f)) != EOF |
| 237 | && !isspace(c)) | 266 | && !isspace(c)) |
| 238 | { | 267 | { |
| 239 | if (unameread == unamelen) | 268 | if (unameread >= unamelen-1) |
| 240 | { | 269 | if (!(username = realloc(username, unamelen *= 2))) |
| 241 | if (!(username = realloc(username, unamelen *= 2))) | 270 | return -1; |
| 242 | return -1; | ||
| 243 | } | ||
| 244 | username[unameread] = c; | 271 | username[unameread] = c; |
| 245 | unameread++; | 272 | unameread++; |
| 246 | } | 273 | } |
| @@ -366,7 +393,11 @@ write_scores(const char *filename, const struct score_entry *scores, | |||
| 366 | return -1; | 393 | return -1; |
| 367 | strcpy(tempfile, filename); | 394 | strcpy(tempfile, filename); |
| 368 | strcat(tempfile, ".tempXXXXXX"); | 395 | strcat(tempfile, ".tempXXXXXX"); |
| 396 | #ifdef HAVE_MKSTEMP | ||
| 369 | if (mkstemp(tempfile) < 0 | 397 | if (mkstemp(tempfile) < 0 |
| 398 | #else | ||
| 399 | if (mktemp(tempfile) != tempfile | ||
| 400 | #endif | ||
| 370 | || !(f = fopen(tempfile, "w"))) | 401 | || !(f = fopen(tempfile, "w"))) |
| 371 | return -1; | 402 | return -1; |
| 372 | for (i = 0; i < count; i++) | 403 | for (i = 0; i < count; i++) |
| @@ -374,60 +405,13 @@ write_scores(const char *filename, const struct score_entry *scores, | |||
| 374 | scores[i].data) < 0) | 405 | scores[i].data) < 0) |
| 375 | return -1; | 406 | return -1; |
| 376 | fclose(f); | 407 | fclose(f); |
| 377 | rename(tempfile, filename); | 408 | if (rename(tempfile, filename) < 0) |
| 378 | return 0; | ||
| 379 | } | ||
| 380 | |||
| 381 | #if 0 | ||
| 382 | int | ||
| 383 | lock_file(const char *filename, void **state) | ||
| 384 | { | ||
| 385 | struct flock lock; | ||
| 386 | int *istate; | ||
| 387 | int fd, attempts = 0, ret = 0; | ||
| 388 | lock.l_type = F_WRLCK; | ||
| 389 | lock.l_whence = SEEK_SET; | ||
| 390 | lock.l_start = 0; | ||
| 391 | lock.l_len = 0; | ||
| 392 | istate = malloc(sizeof(int)); | ||
| 393 | if (!istate) | ||
| 394 | return -1; | 409 | return -1; |
| 395 | if ((fd = open(filename, O_RDWR, 0600)) < 0) | 410 | if (chmod(filename, 0644) < 0) |
| 396 | return -1; | 411 | return -1; |
| 397 | *istate = fd; | 412 | return 0; |
| 398 | trylock: | ||
| 399 | attempts++; | ||
| 400 | if ((ret = fcntl(fd, F_GETLK, &lock)) == -1) | ||
| 401 | { | ||
| 402 | if (ret == EACCES || ret == EAGAIN) | ||
| 403 | if (attempts > MAX_ATTEMPTS) | ||
| 404 | exit(1); | ||
| 405 | else | ||
| 406 | { | ||
| 407 | sleep((rand() % 3)+1); | ||
| 408 | goto trylock; | ||
| 409 | } | ||
| 410 | else | ||
| 411 | ret = 0 ; | ||
| 412 | } | ||
| 413 | else | ||
| 414 | ret = 0; | ||
| 415 | *state = istate; | ||
| 416 | return ret; | ||
| 417 | } | ||
| 418 | |||
| 419 | int | ||
| 420 | unlock_file(const char *filename, void *state) | ||
| 421 | { | ||
| 422 | int fd, ret; | ||
| 423 | fd = *((int *) state); | ||
| 424 | free(state); | ||
| 425 | ret = close(fd); | ||
| 426 | return ret; | ||
| 427 | } | 413 | } |
| 428 | 414 | ||
| 429 | #else | ||
| 430 | |||
| 431 | int | 415 | int |
| 432 | lock_file(const char *filename, void **state) | 416 | lock_file(const char *filename, void **state) |
| 433 | { | 417 | { |
| @@ -450,8 +434,7 @@ lock_file(const char *filename, void **state) | |||
| 450 | lose some scores. */ | 434 | lose some scores. */ |
| 451 | if (attempts > MAX_ATTEMPTS) | 435 | if (attempts > MAX_ATTEMPTS) |
| 452 | unlink(lockpath); | 436 | unlink(lockpath); |
| 453 | else | 437 | sleep((rand() % 2)+1); |
| 454 | sleep((rand() % 2)+1); | ||
| 455 | goto trylock; | 438 | goto trylock; |
| 456 | } | 439 | } |
| 457 | else | 440 | else |
| @@ -471,5 +454,3 @@ unlock_file(const char *filename, void *state) | |||
| 471 | errno = saved_errno; | 454 | errno = saved_errno; |
| 472 | return ret; | 455 | return ret; |
| 473 | } | 456 | } |
| 474 | |||
| 475 | #endif | ||