69 |
#endif |
#endif |
70 |
|
|
71 |
int |
int |
72 |
usage(err) |
usage (err) |
73 |
int err; |
int err; |
74 |
{ |
{ |
75 |
fprintf(stdout, "Usage: update-game-score [-m MAX ] [ -r ] game/scorefile SCORE DATA\n"); |
fprintf (stdout, "Usage: update-game-score [-m MAX ] [ -r ] game/scorefile SCORE DATA\n"); |
76 |
fprintf(stdout, " update-game-score -h\n"); |
fprintf (stdout, " update-game-score -h\n"); |
77 |
fprintf(stdout, " -h\t\tDisplay this help.\n"); |
fprintf (stdout, " -h\t\tDisplay this help.\n"); |
78 |
fprintf(stdout, " -m MAX\t\tLimit the maximum number of scores to MAX.\n"); |
fprintf (stdout, " -m MAX\t\tLimit the maximum number of scores to MAX.\n"); |
79 |
fprintf(stdout, " -r\t\tSort the scores in increasing order.\n"); |
fprintf (stdout, " -r\t\tSort the scores in increasing order.\n"); |
80 |
fprintf(stdout, " -d DIR\t\tStore scores in DIR (only if not setuid).\n"); |
fprintf (stdout, " -d DIR\t\tStore scores in DIR (only if not setuid).\n"); |
81 |
exit(err); |
exit (err); |
82 |
} |
} |
83 |
|
|
84 |
int |
int lock_file P_ ((const char *filename, void **state)); |
85 |
lock_file P_((const char *filename, void **state)); |
int unlock_file P_ ((const char *filename, void *state)); |
|
int |
|
|
unlock_file P_((const char *filename, void *state)); |
|
86 |
|
|
87 |
struct score_entry |
struct score_entry |
88 |
{ |
{ |
91 |
char *data; |
char *data; |
92 |
}; |
}; |
93 |
|
|
94 |
int |
int read_scores P_ ((const char *filename, struct score_entry **scores, |
95 |
read_scores P_((const char *filename, struct score_entry **scores, |
int *count)); |
96 |
int *count)); |
int push_score P_ ((struct score_entry **scores, int *count, |
97 |
int |
int newscore, char *username, char *newdata)); |
98 |
push_score P_((struct score_entry **scores, int *count, |
void sort_scores P_ ((struct score_entry *scores, int count, int reverse)); |
99 |
int newscore, char *username, char *newdata)); |
int write_scores P_ ((const char *filename, const struct score_entry *scores, |
100 |
void |
int count)); |
|
sort_scores P_((struct score_entry *scores, int count, int reverse)); |
|
|
int |
|
|
write_scores P_((const char *filename, const struct score_entry *scores, |
|
|
int count)); |
|
101 |
|
|
102 |
void lose P_((const char *msg)) NO_RETURN; |
void lose P_ ((const char *msg)) NO_RETURN; |
103 |
|
|
104 |
void lose(msg) |
void |
105 |
|
lose (msg) |
106 |
const char *msg; |
const char *msg; |
107 |
{ |
{ |
108 |
fprintf(stderr, "%s\n", msg); |
fprintf (stderr, "%s\n", msg); |
109 |
exit(1); |
exit (1); |
110 |
} |
} |
111 |
|
|
112 |
void lose_syserr P_((const char *msg)) NO_RETURN; |
void lose_syserr P_ ((const char *msg)) NO_RETURN; |
113 |
|
|
114 |
void lose_syserr(msg) |
void |
115 |
|
lose_syserr (msg) |
116 |
const char *msg; |
const char *msg; |
117 |
{ |
{ |
118 |
fprintf(stderr, "%s: %s\n", msg, strerror(errno)); |
fprintf (stderr, "%s: %s\n", msg, strerror (errno)); |
119 |
exit(1); |
exit (1); |
120 |
} |
} |
121 |
|
|
122 |
char * |
char * |
123 |
get_user_id P_ ((void)) |
get_user_id P_ ((void)) |
124 |
{ |
{ |
125 |
char *name; |
char *name; |
126 |
struct passwd *buf = getpwuid(getuid()); |
struct passwd *buf = getpwuid (getuid ()); |
127 |
if (!buf) |
if (!buf) |
128 |
{ |
{ |
129 |
int count = 1; |
int count = 1; |
130 |
int uid = (int) getuid(); |
int uid = (int) getuid (); |
131 |
int tuid = uid; |
int tuid = uid; |
132 |
while (tuid /= 10) |
while (tuid /= 10) |
133 |
count++; |
count++; |
134 |
name = malloc(count+1); |
name = malloc (count+1); |
135 |
if (!name) |
if (!name) |
136 |
return NULL; |
return NULL; |
137 |
sprintf(name, "%d", uid); |
sprintf (name, "%d", uid); |
138 |
return name; |
return name; |
139 |
} |
} |
140 |
return buf->pw_name; |
return buf->pw_name; |
141 |
} |
} |
142 |
|
|
143 |
char * |
char * |
144 |
get_prefix(running_suid, user_prefix) |
get_prefix (running_suid, user_prefix) |
145 |
int running_suid; |
int running_suid; |
146 |
char *user_prefix; |
char *user_prefix; |
147 |
{ |
{ |
148 |
if (!running_suid && user_prefix == NULL) |
if (!running_suid && user_prefix == NULL) |
149 |
lose("Not using a shared game directory, and no prefix given."); |
lose ("Not using a shared game directory, and no prefix given."); |
150 |
if (running_suid) |
if (running_suid) |
151 |
{ |
{ |
152 |
#ifdef HAVE_SHARED_GAME_DIR |
#ifdef HAVE_SHARED_GAME_DIR |
153 |
return HAVE_SHARED_GAME_DIR; |
return HAVE_SHARED_GAME_DIR; |
154 |
#else |
#else |
155 |
lose("This program was compiled without HAVE_SHARED_GAME_DIR,\n and should not be suid."); |
lose ("This program was compiled without HAVE_SHARED_GAME_DIR,\n and should not be suid."); |
156 |
#endif |
#endif |
157 |
} |
} |
158 |
return user_prefix; |
return user_prefix; |
159 |
} |
} |
160 |
|
|
161 |
int |
int |
162 |
main(argc, argv) |
main (argc, argv) |
163 |
int argc; |
int argc; |
164 |
char **argv; |
char **argv; |
165 |
{ |
{ |
171 |
int newscore, scorecount, reverse = 0, max = MAX_SCORES; |
int newscore, scorecount, reverse = 0, max = MAX_SCORES; |
172 |
char *newdata; |
char *newdata; |
173 |
|
|
174 |
srand(time(0)); |
srand (time (0)); |
175 |
|
|
176 |
while ((c = getopt(argc, argv, "hrm:d:")) != -1) |
while ((c = getopt (argc, argv, "hrm:d:")) != -1) |
177 |
switch (c) |
switch (c) |
178 |
{ |
{ |
179 |
case 'h': |
case 'h': |
180 |
usage(0); |
usage (0); |
181 |
break; |
break; |
182 |
case 'd': |
case 'd': |
183 |
user_prefix = optarg; |
user_prefix = optarg; |
186 |
reverse = 1; |
reverse = 1; |
187 |
break; |
break; |
188 |
case 'm': |
case 'm': |
189 |
max = atoi(optarg); |
max = atoi (optarg); |
190 |
if (max > MAX_SCORES) |
if (max > MAX_SCORES) |
191 |
max = MAX_SCORES; |
max = MAX_SCORES; |
192 |
break; |
break; |
193 |
default: |
default: |
194 |
usage(1); |
usage (1); |
195 |
} |
} |
196 |
|
|
197 |
if (optind+3 != argc) |
if (optind+3 != argc) |
198 |
usage(1); |
usage (1); |
199 |
|
|
200 |
running_suid = (getuid() != geteuid()); |
running_suid = (getuid () != geteuid ()); |
201 |
|
|
202 |
prefix = get_prefix(running_suid, user_prefix); |
prefix = get_prefix (running_suid, user_prefix); |
203 |
|
|
204 |
scorefile = malloc(strlen(prefix) + strlen(argv[optind]) + 2); |
scorefile = malloc (strlen (prefix) + strlen (argv[optind]) + 2); |
205 |
if (!scorefile) |
if (!scorefile) |
206 |
lose_syserr("Couldn't allocate score file"); |
lose_syserr ("Couldn't allocate score file"); |
207 |
|
|
208 |
strcpy(scorefile, prefix); |
strcpy (scorefile, prefix); |
209 |
strcat(scorefile, "/"); |
strcat (scorefile, "/"); |
210 |
strcat(scorefile, argv[optind]); |
strcat (scorefile, argv[optind]); |
211 |
newscore = atoi(argv[optind+1]); |
newscore = atoi (argv[optind+1]); |
212 |
newdata = argv[optind+2]; |
newdata = argv[optind+2]; |
213 |
if (strlen(newdata) > MAX_DATA_LEN) |
if (strlen (newdata) > MAX_DATA_LEN) |
214 |
newdata[MAX_DATA_LEN] = '\0'; |
newdata[MAX_DATA_LEN] = '\0'; |
215 |
|
|
216 |
if ((user_id = get_user_id()) == NULL) |
if ((user_id = get_user_id ()) == NULL) |
217 |
lose_syserr("Couldn't determine user id"); |
lose_syserr ("Couldn't determine user id"); |
218 |
|
|
219 |
if (stat(scorefile, &buf) < 0) |
if (stat (scorefile, &buf) < 0) |
220 |
lose_syserr("Failed to access scores file"); |
lose_syserr ("Failed to access scores file"); |
221 |
|
|
222 |
if (lock_file(scorefile, &lockstate) < 0) |
if (lock_file (scorefile, &lockstate) < 0) |
223 |
lose_syserr("Failed to lock scores file"); |
lose_syserr ("Failed to lock scores file"); |
224 |
|
|
225 |
if (read_scores(scorefile, &scores, &scorecount) < 0) |
if (read_scores (scorefile, &scores, &scorecount) < 0) |
226 |
{ |
{ |
227 |
unlock_file(scorefile, lockstate); |
unlock_file (scorefile, lockstate); |
228 |
lose_syserr("Failed to read scores file"); |
lose_syserr ("Failed to read scores file"); |
229 |
} |
} |
230 |
push_score(&scores, &scorecount, newscore, user_id, newdata); |
push_score (&scores, &scorecount, newscore, user_id, newdata); |
231 |
/* Limit the number of scores. If we're using reverse sorting, then |
/* Limit the number of scores. If we're using reverse sorting, then |
232 |
we should increment the beginning of the array, to skip over the |
we should increment the beginning of the array, to skip over the |
233 |
*smallest* scores. Otherwise, we just decrement the number of |
*smallest* scores. Otherwise, we just decrement the number of |
236 |
scorecount -= (scorecount - MAX_SCORES); |
scorecount -= (scorecount - MAX_SCORES); |
237 |
if (reverse) |
if (reverse) |
238 |
scores += (scorecount - MAX_SCORES); |
scores += (scorecount - MAX_SCORES); |
239 |
sort_scores(scores, scorecount, reverse); |
sort_scores (scores, scorecount, reverse); |
240 |
if (write_scores(scorefile, scores, scorecount) < 0) |
if (write_scores (scorefile, scores, scorecount) < 0) |
241 |
{ |
{ |
242 |
unlock_file(scorefile, lockstate); |
unlock_file (scorefile, lockstate); |
243 |
lose_syserr("Failed to write scores file"); |
lose_syserr ("Failed to write scores file"); |
244 |
} |
} |
245 |
unlock_file(scorefile, lockstate); |
unlock_file (scorefile, lockstate); |
246 |
exit(0); |
exit (0); |
247 |
} |
} |
248 |
|
|
249 |
int |
int |
250 |
read_score(f, score) |
read_score (f, score) |
251 |
FILE *f; |
FILE *f; |
252 |
struct score_entry *score; |
struct score_entry *score; |
253 |
{ |
{ |
254 |
int c; |
int c; |
255 |
if (feof(f)) |
if (feof (f)) |
256 |
return 1; |
return 1; |
257 |
while ((c = getc(f)) != EOF |
while ((c = getc (f)) != EOF |
258 |
&& isdigit(c)) |
&& isdigit (c)) |
259 |
{ |
{ |
260 |
score->score *= 10; |
score->score *= 10; |
261 |
score->score += (c-48); |
score->score += (c-48); |
262 |
} |
} |
263 |
while ((c = getc(f)) != EOF |
while ((c = getc (f)) != EOF |
264 |
&& isspace(c)) |
&& isspace (c)) |
265 |
; |
; |
266 |
if (c == EOF) |
if (c == EOF) |
267 |
return -1; |
return -1; |
268 |
ungetc(c, f); |
ungetc (c, f); |
269 |
#ifdef HAVE_GETDELIM |
#ifdef HAVE_GETDELIM |
270 |
{ |
{ |
271 |
size_t count = 0; |
size_t count = 0; |
272 |
if (getdelim(&score->username, &count, ' ', f) < 1 |
if (getdelim (&score->username, &count, ' ', f) < 1 |
273 |
|| score->username == NULL) |
|| score->username == NULL) |
274 |
return -1; |
return -1; |
275 |
/* Trim the space */ |
/* Trim the space */ |
276 |
score->username[strlen(score->username)-1] = '\0'; |
score->username[strlen (score->username)-1] = '\0'; |
277 |
} |
} |
278 |
#else |
#else |
279 |
{ |
{ |
280 |
int unameread = 0; |
int unameread = 0; |
281 |
int unamelen = 30; |
int unamelen = 30; |
282 |
char *username = malloc(unamelen); |
char *username = malloc (unamelen); |
283 |
if (!username) |
if (!username) |
284 |
return -1; |
return -1; |
285 |
|
|
286 |
while ((c = getc(f)) != EOF |
while ((c = getc (f)) != EOF |
287 |
&& !isspace(c)) |
&& !isspace (c)) |
288 |
{ |
{ |
289 |
if (unameread >= unamelen-1) |
if (unameread >= unamelen-1) |
290 |
if (!(username = realloc(username, unamelen *= 2))) |
if (!(username = realloc (username, unamelen *= 2))) |
291 |
return -1; |
return -1; |
292 |
username[unameread] = c; |
username[unameread] = c; |
293 |
unameread++; |
unameread++; |
303 |
errno = 0; |
errno = 0; |
304 |
{ |
{ |
305 |
size_t len; |
size_t len; |
306 |
if (getline(&score->data, &len, f) < 0) |
if (getline (&score->data, &len, f) < 0) |
307 |
return -1; |
return -1; |
308 |
score->data[strlen(score->data)-1] = '\0'; |
score->data[strlen (score->data)-1] = '\0'; |
309 |
} |
} |
310 |
#else |
#else |
311 |
{ |
{ |
312 |
int cur = 0; |
int cur = 0; |
313 |
int len = 16; |
int len = 16; |
314 |
char *buf = malloc(len); |
char *buf = malloc (len); |
315 |
if (!buf) |
if (!buf) |
316 |
return -1; |
return -1; |
317 |
while ((c = getc(f)) != EOF |
while ((c = getc (f)) != EOF |
318 |
&& c != '\n') |
&& c != '\n') |
319 |
{ |
{ |
320 |
if (cur >= len-1) |
if (cur >= len-1) |
321 |
{ |
{ |
322 |
if (!(buf = realloc(buf, len *= 2))) |
if (!(buf = realloc (buf, len *= 2))) |
323 |
return -1; |
return -1; |
324 |
} |
} |
325 |
buf[cur] = c; |
buf[cur] = c; |
333 |
} |
} |
334 |
|
|
335 |
int |
int |
336 |
read_scores(filename, scores, count) |
read_scores (filename, scores, count) |
337 |
const char *filename; |
const char *filename; |
338 |
struct score_entry **scores; |
struct score_entry **scores; |
339 |
int *count; |
int *count; |
340 |
{ |
{ |
341 |
int readval, scorecount, cursize; |
int readval, scorecount, cursize; |
342 |
struct score_entry *ret; |
struct score_entry *ret; |
343 |
FILE *f = fopen(filename, "r"); |
FILE *f = fopen (filename, "r"); |
344 |
if (!f) |
if (!f) |
345 |
return -1; |
return -1; |
346 |
scorecount = 0; |
scorecount = 0; |
347 |
cursize = 16; |
cursize = 16; |
348 |
ret = malloc(sizeof(struct score_entry) * cursize); |
ret = malloc (sizeof (struct score_entry) * cursize); |
349 |
if (!ret) |
if (!ret) |
350 |
return -1; |
return -1; |
351 |
while ((readval = read_score(f, &ret[scorecount])) == 0) |
while ((readval = read_score (f, &ret[scorecount])) == 0) |
352 |
{ |
{ |
353 |
/* We encoutered an error */ |
/* We encoutered an error */ |
354 |
if (readval < 0) |
if (readval < 0) |
356 |
scorecount++; |
scorecount++; |
357 |
if (scorecount >= cursize) |
if (scorecount >= cursize) |
358 |
{ |
{ |
359 |
ret = realloc(ret, cursize *= 2); |
ret = realloc (ret, cursize *= 2); |
360 |
if (!ret) |
if (!ret) |
361 |
return -1; |
return -1; |
362 |
} |
} |
367 |
} |
} |
368 |
|
|
369 |
int |
int |
370 |
score_compare(a, b) |
score_compare (a, b) |
371 |
const void *a; |
const void *a; |
372 |
const void *b; |
const void *b; |
373 |
{ |
{ |
377 |
} |
} |
378 |
|
|
379 |
int |
int |
380 |
score_compare_reverse(a, b) |
score_compare_reverse (a, b) |
381 |
const void *a; |
const void *a; |
382 |
const void *b; |
const void *b; |
383 |
{ |
{ |
387 |
} |
} |
388 |
|
|
389 |
int |
int |
390 |
push_score(scores, count, newscore, username, newdata) |
push_score (scores, count, newscore, username, newdata) |
391 |
struct score_entry **scores; |
struct score_entry **scores; |
392 |
int *count; int newscore; |
int *count; int newscore; |
393 |
char *username; |
char *username; |
394 |
char *newdata; |
char *newdata; |
395 |
{ |
{ |
396 |
struct score_entry *newscores = realloc(*scores, |
struct score_entry *newscores |
397 |
sizeof(struct score_entry) * ((*count) + 1)); |
= realloc (*scores, |
398 |
|
sizeof (struct score_entry) * ((*count) + 1)); |
399 |
if (!newscores) |
if (!newscores) |
400 |
return -1; |
return -1; |
401 |
newscores[*count].score = newscore; |
newscores[*count].score = newscore; |
407 |
} |
} |
408 |
|
|
409 |
void |
void |
410 |
sort_scores(scores, count, reverse) |
sort_scores (scores, count, reverse) |
411 |
struct score_entry *scores; |
struct score_entry *scores; |
412 |
int count; |
int count; |
413 |
int reverse; |
int reverse; |
414 |
{ |
{ |
415 |
qsort(scores, count, sizeof(struct score_entry), |
qsort (scores, count, sizeof (struct score_entry), |
416 |
reverse ? score_compare_reverse : score_compare); |
reverse ? score_compare_reverse : score_compare); |
417 |
} |
} |
418 |
|
|
419 |
int |
int |
420 |
write_scores(filename, scores, count) |
write_scores (filename, scores, count) |
421 |
const char *filename; |
const char *filename; |
422 |
const struct score_entry * scores; |
const struct score_entry * scores; |
423 |
int count; |
int count; |
424 |
{ |
{ |
425 |
FILE *f; |
FILE *f; |
426 |
int i; |
int i; |
427 |
char *tempfile = malloc(strlen(filename) + strlen(".tempXXXXXX") + 1); |
char *tempfile = malloc (strlen (filename) + strlen (".tempXXXXXX") + 1); |
428 |
if (!tempfile) |
if (!tempfile) |
429 |
return -1; |
return -1; |
430 |
strcpy(tempfile, filename); |
strcpy (tempfile, filename); |
431 |
strcat(tempfile, ".tempXXXXXX"); |
strcat (tempfile, ".tempXXXXXX"); |
432 |
#ifdef HAVE_MKSTEMP |
#ifdef HAVE_MKSTEMP |
433 |
if (mkstemp(tempfile) < 0 |
if (mkstemp (tempfile) < 0 |
434 |
#else |
#else |
435 |
if (mktemp(tempfile) != tempfile |
if (mktemp (tempfile) != tempfile |
436 |
#endif |
#endif |
437 |
|| !(f = fopen(tempfile, "w"))) |
|| !(f = fopen (tempfile, "w"))) |
438 |
return -1; |
return -1; |
439 |
for (i = 0; i < count; i++) |
for (i = 0; i < count; i++) |
440 |
if (fprintf(f, "%ld %s %s\n", scores[i].score, scores[i].username, |
if (fprintf (f, "%ld %s %s\n", scores[i].score, scores[i].username, |
441 |
scores[i].data) < 0) |
scores[i].data) < 0) |
442 |
return -1; |
return -1; |
443 |
fclose(f); |
fclose (f); |
444 |
if (rename(tempfile, filename) < 0) |
if (rename (tempfile, filename) < 0) |
445 |
return -1; |
return -1; |
446 |
if (chmod(filename, 0644) < 0) |
if (chmod (filename, 0644) < 0) |
447 |
return -1; |
return -1; |
448 |
return 0; |
return 0; |
449 |
} |
} |
450 |
|
|
451 |
int |
int |
452 |
lock_file(filename, state) |
lock_file (filename, state) |
453 |
const char *filename; |
const char *filename; |
454 |
void **state; |
void **state; |
455 |
{ |
{ |
457 |
struct stat buf; |
struct stat buf; |
458 |
int attempts = 0; |
int attempts = 0; |
459 |
char *lockext = ".lockfile"; |
char *lockext = ".lockfile"; |
460 |
char *lockpath = malloc(strlen(filename) + strlen(lockext) + 60); |
char *lockpath = malloc (strlen (filename) + strlen (lockext) + 60); |
461 |
if (!lockpath) |
if (!lockpath) |
462 |
return -1; |
return -1; |
463 |
strcpy(lockpath, filename); |
strcpy (lockpath, filename); |
464 |
strcat(lockpath, lockext); |
strcat (lockpath, lockext); |
465 |
*state = lockpath; |
*state = lockpath; |
466 |
trylock: |
trylock: |
467 |
attempts++; |
attempts++; |
468 |
/* If the lock is over an hour old, delete it. */ |
/* If the lock is over an hour old, delete it. */ |
469 |
if (stat(lockpath, &buf) == 0 |
if (stat (lockpath, &buf) == 0 |
470 |
&& (difftime(buf.st_ctime, time(NULL) > 60*60))) |
&& (difftime (buf.st_ctime, time (NULL) > 60*60))) |
471 |
unlink(lockpath); |
unlink (lockpath); |
472 |
if ((fd = open(lockpath, O_CREAT | O_EXCL, 0600)) < 0) |
if ((fd = open (lockpath, O_CREAT | O_EXCL, 0600)) < 0) |
473 |
{ |
{ |
474 |
if (errno == EEXIST) |
if (errno == EEXIST) |
475 |
{ |
{ |
477 |
lose some scores. */ |
lose some scores. */ |
478 |
if (attempts > MAX_ATTEMPTS) |
if (attempts > MAX_ATTEMPTS) |
479 |
{ |
{ |
480 |
unlink(lockpath); |
unlink (lockpath); |
481 |
attempts = 0; |
attempts = 0; |
482 |
} |
} |
483 |
sleep((rand() % 2)+1); |
sleep ((rand () % 2)+1); |
484 |
goto trylock; |
goto trylock; |
485 |
} |
} |
486 |
else |
else |
487 |
return -1; |
return -1; |
488 |
} |
} |
489 |
close(fd); |
close (fd); |
490 |
return 0; |
return 0; |
491 |
} |
} |
492 |
|
|
493 |
int |
int |
494 |
unlock_file(filename, state) |
unlock_file (filename, state) |
495 |
const char *filename; |
const char *filename; |
496 |
void *state; |
void *state; |
497 |
{ |
{ |
498 |
char *lockpath = (char *) state; |
char *lockpath = (char *) state; |
499 |
int ret = unlink(lockpath); |
int ret = unlink (lockpath); |
500 |
int saved_errno = errno; |
int saved_errno = errno; |
501 |
free(lockpath); |
free (lockpath); |
502 |
errno = saved_errno; |
errno = saved_errno; |
503 |
return ret; |
return ret; |
504 |
} |
} |