1 /* update-game-score.c --- Update a score file
2 Copyright (C) 2002 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
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)
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.
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. */
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
25 Created 2002/03/22, by Colin Walters <walters@debian.org>
39 // #include "config.h"
41 #define MAX_ATTEMPTS 5
42 #define SCORE_FILE_PREFIX "/var/games/emacs/"
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");
56 lock_file(const char *filename
, void **state
);
58 unlock_file(const char *filename
, void *state
);
67 read_scores(const char *filename
, struct score_entry
**scores
,
70 push_score(struct score_entry
**scores
, int *count
,
71 int newscore
, char *newdata
);
73 sort_scores(struct score_entry
*scores
, int count
, int reverse
);
75 write_scores(const char *filename
, const struct score_entry
*scores
,
79 main(int argc
, char **argv
)
85 struct score_entry
*scores
;
86 int newscore
, scorecount
, reverse
= 0, max
= -1;
91 while ((c
= getopt(argc
, argv
, "hrm:")) != -1)
107 if (optind
+3 != argc
)
109 scorefile
= malloc(strlen(SCORE_FILE_PREFIX
) + strlen(argv
[optind
]) + 20);
112 fprintf(stderr
, "Couldn't create score file name: %s\n",
116 strcpy(scorefile
, SCORE_FILE_PREFIX
);
117 strcat(scorefile
, argv
[optind
]);
118 newscore
= atoi(argv
[optind
+1]);
119 newdata
= argv
[optind
+2];
121 if (stat(scorefile
, &buf
) < 0)
123 fprintf(stderr
, "Failed to access scores file \"%s\": %s\n",
124 scorefile
, strerror(errno
));
127 if (lock_file(scorefile
, &lockstate
) < 0)
129 fprintf(stderr
, "Failed to lock scores file \"%s\": %s\n",
130 scorefile
, strerror(errno
));
133 if (read_scores(scorefile
, &scores
, &scorecount
) < 0)
135 fprintf(stderr
, "Failed to read scores file \"%s\": %s\n",
136 scorefile
, strerror(errno
));
139 push_score(&scores
, &scorecount
, newscore
, newdata
);
140 sort_scores(scores
, scorecount
, reverse
);
141 if (write_scores(scorefile
, scores
, scorecount
) < 0)
143 fprintf(stderr
, "Failed to write scores file \"%s\": %s\n",
144 scorefile
, strerror(errno
));
147 unlock_file(scorefile
, lockstate
);
150 unlock_file(scorefile
, lockstate
);
156 read_score(FILE *f
, struct score_entry
*score
)
161 while ((c
= getc(f
)) != EOF
164 score
->score
+= (c
-48);
171 if (getline(&score
->data
, &len
, f
) < 0)
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
))
182 /* Trim the newline */
183 score
->data
[strlen(score
->data
)-1] = '\0';
188 read_scores(const char *filename
, struct score_entry
**scores
,
191 int readval
, scorecount
, cursize
;
192 struct score_entry
*ret
;
193 FILE *f
= fopen(filename
, "r");
198 ret
= malloc(sizeof(struct score_entry
) * cursize
);
201 while ((readval
= read_score(f
, &ret
[scorecount
])) == 0)
203 /* We encoutered an error */
207 if (scorecount
>= cursize
)
209 ret
= realloc(ret
, cursize
*= 2);
220 score_compare(const void *a
, const void *b
)
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
);
228 score_compare_reverse(const void *a
, const void *b
)
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
);
236 push_score(struct score_entry
**scores
, int *count
,
237 int newscore
, char *newdata
)
239 struct score_entry
*newscores
= realloc(*scores
,
240 sizeof(struct score_entry
) * ((*count
) + 1));
243 newscores
[*count
].score
= newscore
;
244 newscores
[*count
].data
= newdata
;
251 sort_scores(struct score_entry
*scores
, int count
, int reverse
)
253 qsort(scores
, count
, sizeof(struct score_entry
),
254 reverse
? score_compare_reverse
: score_compare
);
258 write_scores(const char *filename
, const struct score_entry
*scores
,
263 char *tempfile
= malloc(strlen(filename
) + strlen(".tempXXXXXX") + 1);
266 strcpy(tempfile
, filename
);
267 strcat(tempfile
, ".tempXXXXXX");
268 if (mkstemp(tempfile
) < 0
269 || !(f
= fopen(tempfile
, "w")))
271 for (i
= 0; i
< count
; i
++)
272 if (fprintf(f
, "%ld %s\n", scores
[i
].score
, scores
[i
].data
) < 0)
275 rename(tempfile
, filename
);
281 lock_file(const char *filename
, void **state
)
285 int fd
, attempts
= 0, ret
= 0;
286 lock
.l_type
= F_WRLCK
;
287 lock
.l_whence
= SEEK_SET
;
290 istate
= malloc(sizeof(int));
293 if ((fd
= open(filename
, O_RDWR
, 0600)) < 0)
298 if ((ret
= fcntl(fd
, F_GETLK
, &lock
)) == -1)
300 if (ret
== EACCES
|| ret
== EAGAIN
)
301 if (attempts
> MAX_ATTEMPTS
)
305 sleep((rand() % 3)+1);
318 unlock_file(const char *filename
, void *state
)
321 fd
= *((int *) state
);
330 lock_file(const char *filename
, void **state
)
334 char *lockext
= ".lockfile";
335 char *lockpath
= malloc(strlen(filename
) + strlen(lockext
) + 60);
338 strcpy(lockpath
, filename
);
339 strcat(lockpath
, lockext
);
343 if ((fd
= open(lockpath
, O_CREAT
| O_EXCL
, 0600)) < 0)
347 /* Break the lock; we won't corrupt the file, but we might
349 if (attempts
> MAX_ATTEMPTS
)
352 sleep((rand() % 2)+1);
363 unlock_file(const char *filename
, void *state
)
365 char *lockpath
= (char *) state
;
366 int ret
= unlink(lockpath
);
367 int saved_errno
= errno
;