Mercurial > emacs
comparison lib-src/update-game-score.c @ 44184:dca60454e83f
Initial version.
author | Colin Walters <walters@gnu.org> |
---|---|
date | Wed, 27 Mar 2002 20:57:06 +0000 |
parents | |
children | 968fd5ccea65 |
comparison
equal
deleted
inserted
replaced
44183:3de18bf7053f | 44184:dca60454e83f |
---|---|
1 /* update-game-score.c --- Update a score file | |
2 Copyright (C) 2002 Free Software Foundation, Inc. | |
3 | |
4 This file is part of GNU Emacs. | |
5 | |
6 GNU Emacs is free software; you can redistribute it and/or modify | |
7 it under the terms of the GNU General Public License as published by | |
8 the Free Software Foundation; either version 2, or (at your option) | |
9 any later version. | |
10 | |
11 GNU Emacs is distributed in the hope that it will be useful, | |
12 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 GNU General Public License for more details. | |
15 | |
16 You should have received a copy of the GNU General Public License | |
17 along with GNU Emacs; see the file COPYING. If not, write to | |
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
19 Boston, MA 02111-1307, USA. */ | |
20 | |
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 | |
23 group like `games'. | |
24 | |
25 Created 2002/03/22, by Colin Walters <walters@debian.org> | |
26 */ | |
27 | |
28 #define _GNU_SOURCE | |
29 | |
30 #include <unistd.h> | |
31 #include <errno.h> | |
32 #include <string.h> | |
33 #include <stdlib.h> | |
34 #include <stdio.h> | |
35 #include <time.h> | |
36 #include <ctype.h> | |
37 #include <fcntl.h> | |
38 #include <sys/stat.h> | |
39 // #include "config.h" | |
40 | |
41 #define MAX_ATTEMPTS 5 | |
42 #define SCORE_FILE_PREFIX "/var/games/emacs/" | |
43 | |
44 int | |
45 usage(int err) | |
46 { | |
47 fprintf(stdout, "Usage: update-game-score [-m MAX ] [ -r ] game/scorefile SCORE DATA\n"); | |
48 fprintf(stdout, " update-game-score -h\n"); | |
49 fprintf(stdout, " -h\t\tDisplay this help.\n"); | |
50 fprintf(stdout, " -m MAX\t\tLimit the maximum number of scores to MAX.\n"); | |
51 fprintf(stdout, " -r\t\tSort the scores in increasing order.\n"); | |
52 exit(err); | |
53 } | |
54 | |
55 int | |
56 lock_file(const char *filename, void **state); | |
57 int | |
58 unlock_file(const char *filename, void *state); | |
59 | |
60 struct score_entry | |
61 { | |
62 long score; | |
63 char *data; | |
64 }; | |
65 | |
66 int | |
67 read_scores(const char *filename, struct score_entry **scores, | |
68 int *count); | |
69 int | |
70 push_score(struct score_entry **scores, int *count, | |
71 int newscore, char *newdata); | |
72 void | |
73 sort_scores(struct score_entry *scores, int count, int reverse); | |
74 int | |
75 write_scores(const char *filename, const struct score_entry *scores, | |
76 int count); | |
77 | |
78 int | |
79 main(int argc, char **argv) | |
80 { | |
81 int c; | |
82 void *lockstate; | |
83 char *scorefile; | |
84 struct stat buf; | |
85 struct score_entry *scores; | |
86 int newscore, scorecount, reverse = 0, max = -1; | |
87 char *newdata; | |
88 | |
89 srand(time(0)); | |
90 | |
91 while ((c = getopt(argc, argv, "hrm:")) != -1) | |
92 switch (c) | |
93 { | |
94 case 'h': | |
95 usage(0); | |
96 break; | |
97 case 'r': | |
98 reverse = 1; | |
99 break; | |
100 case 'm': | |
101 max = atoi(optarg); | |
102 break; | |
103 default: | |
104 usage(1); | |
105 } | |
106 | |
107 if (optind+3 != argc) | |
108 usage(1); | |
109 scorefile = malloc(strlen(SCORE_FILE_PREFIX) + strlen(argv[optind]) + 20); | |
110 if (!scorefile) | |
111 { | |
112 fprintf(stderr, "Couldn't create score file name: %s\n", | |
113 strerror(errno)); | |
114 goto fail; | |
115 } | |
116 strcpy(scorefile, SCORE_FILE_PREFIX); | |
117 strcat(scorefile, argv[optind]); | |
118 newscore = atoi(argv[optind+1]); | |
119 newdata = argv[optind+2]; | |
120 | |
121 if (stat(scorefile, &buf) < 0) | |
122 { | |
123 fprintf(stderr, "Failed to access scores file \"%s\": %s\n", | |
124 scorefile, strerror(errno)); | |
125 goto fail; | |
126 } | |
127 if (lock_file(scorefile, &lockstate) < 0) | |
128 { | |
129 fprintf(stderr, "Failed to lock scores file \"%s\": %s\n", | |
130 scorefile, strerror(errno)); | |
131 goto fail; | |
132 } | |
133 if (read_scores(scorefile, &scores, &scorecount) < 0) | |
134 { | |
135 fprintf(stderr, "Failed to read scores file \"%s\": %s\n", | |
136 scorefile, strerror(errno)); | |
137 goto fail_unlock; | |
138 } | |
139 push_score(&scores, &scorecount, newscore, newdata); | |
140 sort_scores(scores, scorecount, reverse); | |
141 if (write_scores(scorefile, scores, scorecount) < 0) | |
142 { | |
143 fprintf(stderr, "Failed to write scores file \"%s\": %s\n", | |
144 scorefile, strerror(errno)); | |
145 goto fail_unlock; | |
146 } | |
147 unlock_file(scorefile, lockstate); | |
148 exit(0); | |
149 fail_unlock: | |
150 unlock_file(scorefile, lockstate); | |
151 fail: | |
152 exit(1); | |
153 } | |
154 | |
155 int | |
156 read_score(FILE *f, struct score_entry *score) | |
157 { | |
158 int c; | |
159 if (feof(f)) | |
160 return 1; | |
161 while ((c = getc(f)) != EOF | |
162 && isdigit(c)) { | |
163 score->score *= 10; | |
164 score->score += (c-48); | |
165 } | |
166 #ifdef HAVE_GETLINE | |
167 score->data = NULL; | |
168 errno = ESUCCES; | |
169 { | |
170 int len; | |
171 if (getline(&score->data, &len, f) < 0) | |
172 return -1; | |
173 } | |
174 #else | |
175 /* We should probably just copy the getline code into here from | |
176 glibc, instead of this halfassed solution. */ | |
177 score->data = malloc(122); | |
178 score->data[0] = '\0'; | |
179 if (!fgets(score->data, 120, f)) | |
180 return -1; | |
181 #endif | |
182 /* Trim the newline */ | |
183 score->data[strlen(score->data)-1] = '\0'; | |
184 return 0; | |
185 } | |
186 | |
187 int | |
188 read_scores(const char *filename, struct score_entry **scores, | |
189 int *count) | |
190 { | |
191 int readval, scorecount, cursize; | |
192 struct score_entry *ret; | |
193 FILE *f = fopen(filename, "r"); | |
194 if (!f) | |
195 return -1; | |
196 scorecount = 0; | |
197 cursize = 16; | |
198 ret = malloc(sizeof(struct score_entry) * cursize); | |
199 if (!ret) | |
200 return -1; | |
201 while ((readval = read_score(f, &ret[scorecount])) == 0) | |
202 { | |
203 /* We encoutered an error */ | |
204 if (readval < 0) | |
205 return -1; | |
206 scorecount++; | |
207 if (scorecount >= cursize) | |
208 { | |
209 ret = realloc(ret, cursize *= 2); | |
210 if (!ret) | |
211 return -1; | |
212 } | |
213 } | |
214 *count = scorecount; | |
215 *scores = ret; | |
216 return 0; | |
217 } | |
218 | |
219 int | |
220 score_compare(const void *a, const void *b) | |
221 { | |
222 const struct score_entry *sa = (const struct score_entry *) a; | |
223 const struct score_entry *sb = (const struct score_entry *) b; | |
224 return (sb->score > sa->score) - (sb->score < sa->score); | |
225 } | |
226 | |
227 int | |
228 score_compare_reverse(const void *a, const void *b) | |
229 { | |
230 const struct score_entry *sa = (const struct score_entry *) a; | |
231 const struct score_entry *sb = (const struct score_entry *) b; | |
232 return (sa->score > sb->score) - (sa->score < sb->score); | |
233 } | |
234 | |
235 int | |
236 push_score(struct score_entry **scores, int *count, | |
237 int newscore, char *newdata) | |
238 { | |
239 struct score_entry *newscores = realloc(*scores, | |
240 sizeof(struct score_entry) * ((*count) + 1)); | |
241 if (!newscores) | |
242 return -1; | |
243 newscores[*count].score = newscore; | |
244 newscores[*count].data = newdata; | |
245 (*count) += 1; | |
246 *scores = newscores; | |
247 return 0; | |
248 } | |
249 | |
250 void | |
251 sort_scores(struct score_entry *scores, int count, int reverse) | |
252 { | |
253 qsort(scores, count, sizeof(struct score_entry), | |
254 reverse ? score_compare_reverse : score_compare); | |
255 } | |
256 | |
257 int | |
258 write_scores(const char *filename, const struct score_entry *scores, | |
259 int count) | |
260 { | |
261 FILE *f; | |
262 int i; | |
263 char *tempfile = malloc(strlen(filename) + strlen(".tempXXXXXX") + 1); | |
264 if (!tempfile) | |
265 return -1; | |
266 strcpy(tempfile, filename); | |
267 strcat(tempfile, ".tempXXXXXX"); | |
268 if (mkstemp(tempfile) < 0 | |
269 || !(f = fopen(tempfile, "w"))) | |
270 return -1; | |
271 for (i = 0; i < count; i++) | |
272 if (fprintf(f, "%ld %s\n", scores[i].score, scores[i].data) < 0) | |
273 return -1; | |
274 fclose(f); | |
275 rename(tempfile, filename); | |
276 return 0; | |
277 } | |
278 | |
279 #if 0 | |
280 int | |
281 lock_file(const char *filename, void **state) | |
282 { | |
283 struct flock lock; | |
284 int *istate; | |
285 int fd, attempts = 0, ret = 0; | |
286 lock.l_type = F_WRLCK; | |
287 lock.l_whence = SEEK_SET; | |
288 lock.l_start = 0; | |
289 lock.l_len = 0; | |
290 istate = malloc(sizeof(int)); | |
291 if (!istate) | |
292 return -1; | |
293 if ((fd = open(filename, O_RDWR, 0600)) < 0) | |
294 return -1; | |
295 *istate = fd; | |
296 trylock: | |
297 attempts++; | |
298 if ((ret = fcntl(fd, F_GETLK, &lock)) == -1) | |
299 { | |
300 if (ret == EACCES || ret == EAGAIN) | |
301 if (attempts > MAX_ATTEMPTS) | |
302 exit(1); | |
303 else | |
304 { | |
305 sleep((rand() % 3)+1); | |
306 goto trylock; | |
307 } | |
308 else | |
309 ret = 0 ; | |
310 } | |
311 else | |
312 ret = 0; | |
313 *state = istate; | |
314 return ret; | |
315 } | |
316 | |
317 int | |
318 unlock_file(const char *filename, void *state) | |
319 { | |
320 int fd, ret; | |
321 fd = *((int *) state); | |
322 free(state); | |
323 ret = close(fd); | |
324 return ret; | |
325 } | |
326 | |
327 #else | |
328 | |
329 int | |
330 lock_file(const char *filename, void **state) | |
331 { | |
332 int fd; | |
333 int attempts = 0; | |
334 char *lockext = ".lockfile"; | |
335 char *lockpath = malloc(strlen(filename) + strlen(lockext) + 60); | |
336 if (!lockpath) | |
337 return -1; | |
338 strcpy(lockpath, filename); | |
339 strcat(lockpath, lockext); | |
340 *state = lockpath; | |
341 trylock: | |
342 attempts++; | |
343 if ((fd = open(lockpath, O_CREAT | O_EXCL, 0600)) < 0) | |
344 { | |
345 if (errno == EEXIST) | |
346 { | |
347 /* Break the lock; we won't corrupt the file, but we might | |
348 lose some scores. */ | |
349 if (attempts > MAX_ATTEMPTS) | |
350 unlink(lockpath); | |
351 else | |
352 sleep((rand() % 2)+1); | |
353 goto trylock; | |
354 } | |
355 else | |
356 return -1; | |
357 } | |
358 close(fd); | |
359 return 0; | |
360 } | |
361 | |
362 int | |
363 unlock_file(const char *filename, void *state) | |
364 { | |
365 char *lockpath = (char *) state; | |
366 int ret = unlink(lockpath); | |
367 int saved_errno = errno; | |
368 free(lockpath); | |
369 errno = saved_errno; | |
370 return ret; | |
371 } | |
372 | |
373 #endif |