aboutsummaryrefslogtreecommitdiffstats
path: root/lib-src/update-game-score.c
diff options
context:
space:
mode:
authorColin Walters2002-04-10 19:10:23 +0000
committerColin Walters2002-04-10 19:10:23 +0000
commit8eec804f6efd3023e00a8b3f3f22103c2aabe9a9 (patch)
tree570fb1d10763b35e6aec1cb0c14d4c8185605f59 /lib-src/update-game-score.c
parent1ba1fb11c0d8516249894ac08396152c3e1cd009 (diff)
downloademacs-8eec804f6efd3023e00a8b3f3f22103c2aabe9a9.tar.gz
emacs-8eec804f6efd3023e00a8b3f3f22103c2aabe9a9.zip
(toplevel): Include stdarg.h.
(MAX_DATA_LEN, MAX_SCORES): New. (SCORE_FILE_PREFIX): If HAVE_SHARED_GAME_DIR is not defined, default to ~/.emacs.d/games. (get_user_id): Don't zero uid in the case where we can't get the username. (lose): New function. (main): Actually use `max', and default it to MAX_SCORES. Correctly handle new default for SCORE_FILE_PREFIX. Use `lose' function. (read_score): Handle the case of reading unamelen characters, then finishing. Use mktemp if mkstemp isn't available. (lock_file, unlock_file): Delete unused versions. (lock_file): Always sleep, even if we unlinked the lock file.
Diffstat (limited to 'lib-src/update-game-score.c')
-rw-r--r--lib-src/update-game-score.c179
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,
19Boston, MA 02111-1307, USA. */ 19Boston, 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
50int 61int
@@ -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
122void lose(const char *msg, ...)
123 __attribute__ ((format (printf,1,0), noreturn));
124
125void 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
110int 134int
111main(int argc, char **argv) 135main(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
203int 232int
@@ -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
382int
383lock_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
419int
420unlock_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
431int 415int
432lock_file(const char *filename, void **state) 416lock_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