update-game-score.c 12.2 KB
Newer Older
Colin Walters's avatar
Colin Walters committed
1
/* update-game-score.c --- Update a score file
2

Glenn Morris's avatar
Glenn Morris committed
3
Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
4 5 6
  Free Software Foundation, Inc.

Author: Colin Walters <walters@debian.org>
Colin Walters's avatar
Colin Walters committed
7 8 9

This file is part of GNU Emacs.

10
GNU Emacs is free software: you can redistribute it and/or modify
Colin Walters's avatar
Colin Walters committed
11
it under the terms of the GNU General Public License as published by
12 13
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Colin Walters's avatar
Colin Walters committed
14 15 16 17 18 19 20

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
21 22
along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */

Colin Walters's avatar
Colin Walters committed
23 24

/* This program is allows a game to securely and atomically update a
Colin Walters's avatar
Colin Walters committed
25 26 27 28 29 30
   score file.  It should be installed setuid, owned by an appropriate
   user like `games'.

   Alternatively, it can be compiled without HAVE_SHARED_GAME_DIR
   defined, and in that case it will store scores in the user's home
   directory (it should NOT be setuid).
Colin Walters's avatar
Colin Walters committed
31

32
   Created 2002/03/22.
Colin Walters's avatar
Colin Walters committed
33 34
*/

35 36
#include <config.h>

37
#ifdef HAVE_UNISTD_H
Colin Walters's avatar
Colin Walters committed
38
#include <unistd.h>
39
#endif
Colin Walters's avatar
Colin Walters committed
40
#include <errno.h>
41
#ifdef HAVE_STRING_H
Colin Walters's avatar
Colin Walters committed
42
#include <string.h>
43 44
#endif
#ifdef HAVE_STDLIB_H
Colin Walters's avatar
Colin Walters committed
45
#include <stdlib.h>
46
#endif
Colin Walters's avatar
Colin Walters committed
47 48
#include <stdio.h>
#include <time.h>
Colin Walters's avatar
Colin Walters committed
49
#include <pwd.h>
Colin Walters's avatar
Colin Walters committed
50
#include <ctype.h>
51
#ifdef HAVE_FCNTL_H
Colin Walters's avatar
Colin Walters committed
52
#include <fcntl.h>
53 54
#endif
#ifdef STDC_HEADERS
Colin Walters's avatar
Colin Walters committed
55
#include <stdarg.h>
56
#endif
Colin Walters's avatar
Colin Walters committed
57 58
#include <sys/stat.h>

59 60 61 62
/* Needed for SunOS4, for instance.  */
extern char *optarg;
extern int optind, opterr;

Colin Walters's avatar
Colin Walters committed
63
#define MAX_ATTEMPTS 5
Colin Walters's avatar
Colin Walters committed
64 65
#define MAX_SCORES 200
#define MAX_DATA_LEN 1024
66

67 68 69 70 71 72 73
/* Declare the prototype for a general external function.  */
#if defined (PROTOTYPES) || defined (WINDOWSNT)
#define P_(proto) proto
#else
#define P_(proto) ()
#endif

74 75 76 77 78
#ifndef HAVE_DIFFTIME
/* OK on POSIX (time_t is arithmetic type) modulo overflow in subtraction.  */
#define difftime(t1, t0) (double)((t1) - (t0))
#endif

Colin Walters's avatar
Colin Walters committed
79
int
Richard M. Stallman's avatar
Richard M. Stallman committed
80
usage (err)
81
     int err;
Colin Walters's avatar
Colin Walters committed
82
{
Richard M. Stallman's avatar
Richard M. Stallman committed
83 84 85 86 87 88 89
  fprintf (stdout, "Usage: update-game-score [-m MAX ] [ -r ] game/scorefile SCORE DATA\n");
  fprintf (stdout, "       update-game-score -h\n");
  fprintf (stdout, " -h\t\tDisplay this help.\n");
  fprintf (stdout, " -m MAX\t\tLimit the maximum number of scores to MAX.\n");
  fprintf (stdout, " -r\t\tSort the scores in increasing order.\n");
  fprintf (stdout, " -d DIR\t\tStore scores in DIR (only if not setuid).\n");
  exit (err);
Colin Walters's avatar
Colin Walters committed
90 91
}

Richard M. Stallman's avatar
Richard M. Stallman committed
92 93
int lock_file P_ ((const char *filename, void **state));
int unlock_file P_ ((const char *filename, void *state));
Colin Walters's avatar
Colin Walters committed
94 95 96 97

struct score_entry
{
  long score;
Colin Walters's avatar
Colin Walters committed
98
  char *username;
Colin Walters's avatar
Colin Walters committed
99 100 101
  char *data;
};

Richard M. Stallman's avatar
Richard M. Stallman committed
102 103 104 105 106 107 108
int read_scores P_ ((const char *filename, struct score_entry **scores,
		     int *count));
int push_score P_ ((struct score_entry **scores, int *count,
		    int newscore, char *username, char *newdata));
void 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));
109

Richard M. Stallman's avatar
Richard M. Stallman committed
110
void lose P_ ((const char *msg)) NO_RETURN;
111

Richard M. Stallman's avatar
Richard M. Stallman committed
112 113
void
lose (msg)
114 115
     const char *msg;
{
Richard M. Stallman's avatar
Richard M. Stallman committed
116
  fprintf (stderr, "%s\n", msg);
117
  exit (EXIT_FAILURE);
118
}
Colin Walters's avatar
Colin Walters committed
119

Richard M. Stallman's avatar
Richard M. Stallman committed
120
void lose_syserr P_ ((const char *msg)) NO_RETURN;
Colin Walters's avatar
Colin Walters committed
121

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
/* Taken from sysdep.c.  */
#ifndef HAVE_STRERROR
#ifndef WINDOWSNT
char *
strerror (errnum)
     int errnum;
{
  extern char *sys_errlist[];
  extern int sys_nerr;

  if (errnum >= 0 && errnum < sys_nerr)
    return sys_errlist[errnum];
  return (char *) "Unknown error";
}
#endif /* not WINDOWSNT */
#endif /* ! HAVE_STRERROR */

Richard M. Stallman's avatar
Richard M. Stallman committed
139 140
void
lose_syserr (msg)
141
     const char *msg;
Colin Walters's avatar
Colin Walters committed
142
{
Richard M. Stallman's avatar
Richard M. Stallman committed
143
  fprintf (stderr, "%s: %s\n", msg, strerror (errno));
144
  exit (EXIT_FAILURE);
Colin Walters's avatar
Colin Walters committed
145 146
}

Colin Walters's avatar
Colin Walters committed
147
char *
148
get_user_id P_ ((void))
Colin Walters's avatar
Colin Walters committed
149 150
{
  char *name;
Richard M. Stallman's avatar
Richard M. Stallman committed
151
  struct passwd *buf = getpwuid (getuid ());
Colin Walters's avatar
Colin Walters committed
152 153 154
  if (!buf)
    {
      int count = 1;
Richard M. Stallman's avatar
Richard M. Stallman committed
155
      int uid = (int) getuid ();
Colin Walters's avatar
Colin Walters committed
156 157
      int tuid = uid;
      while (tuid /= 10)
Colin Walters's avatar
Colin Walters committed
158
	count++;
Richard M. Stallman's avatar
Richard M. Stallman committed
159
      name = malloc (count+1);
Colin Walters's avatar
Colin Walters committed
160 161
      if (!name)
	return NULL;
Richard M. Stallman's avatar
Richard M. Stallman committed
162
      sprintf (name, "%d", uid);
Colin Walters's avatar
Colin Walters committed
163 164 165 166 167
      return name;
    }
  return buf->pw_name;
}

168
char *
Richard M. Stallman's avatar
Richard M. Stallman committed
169
get_prefix (running_suid, user_prefix)
170 171
     int running_suid;
     char *user_prefix;
Colin Walters's avatar
Colin Walters committed
172
{
Colin Walters's avatar
Colin Walters committed
173
  if (!running_suid && user_prefix == NULL)
Richard M. Stallman's avatar
Richard M. Stallman committed
174
    lose ("Not using a shared game directory, and no prefix given.");
Colin Walters's avatar
Colin Walters committed
175 176 177 178 179
  if (running_suid)
    {
#ifdef HAVE_SHARED_GAME_DIR
      return HAVE_SHARED_GAME_DIR;
#else
Richard M. Stallman's avatar
Richard M. Stallman committed
180
      lose ("This program was compiled without HAVE_SHARED_GAME_DIR,\n and should not be suid.");
Colin Walters's avatar
Colin Walters committed
181 182 183
#endif
    }
  return user_prefix;
Colin Walters's avatar
Colin Walters committed
184 185
}

Colin Walters's avatar
Colin Walters committed
186
int
Richard M. Stallman's avatar
Richard M. Stallman committed
187
main (argc, argv)
188 189
     int argc;
     char **argv;
Colin Walters's avatar
Colin Walters committed
190
{
Colin Walters's avatar
Colin Walters committed
191
  int c, running_suid;
Colin Walters's avatar
Colin Walters committed
192
  void *lockstate;
Colin Walters's avatar
Colin Walters committed
193
  char *user_id, *scorefile, *prefix, *user_prefix = NULL;
Colin Walters's avatar
Colin Walters committed
194 195
  struct stat buf;
  struct score_entry *scores;
Colin Walters's avatar
Colin Walters committed
196
  int newscore, scorecount, reverse = 0, max = MAX_SCORES;
Colin Walters's avatar
Colin Walters committed
197 198
  char *newdata;

Richard M. Stallman's avatar
Richard M. Stallman committed
199
  srand (time (0));
Colin Walters's avatar
Colin Walters committed
200

Richard M. Stallman's avatar
Richard M. Stallman committed
201
  while ((c = getopt (argc, argv, "hrm:d:")) != -1)
Colin Walters's avatar
Colin Walters committed
202 203 204
    switch (c)
      {
      case 'h':
205
	usage (EXIT_SUCCESS);
Colin Walters's avatar
Colin Walters committed
206
	break;
Colin Walters's avatar
Colin Walters committed
207 208 209
      case 'd':
	user_prefix = optarg;
	break;
Colin Walters's avatar
Colin Walters committed
210 211 212 213
      case 'r':
	reverse = 1;
	break;
      case 'm':
Richard M. Stallman's avatar
Richard M. Stallman committed
214
	max = atoi (optarg);
Colin Walters's avatar
Colin Walters committed
215 216
	if (max > MAX_SCORES)
	  max = MAX_SCORES;
Colin Walters's avatar
Colin Walters committed
217 218
	break;
      default:
219
	usage (EXIT_FAILURE);
Colin Walters's avatar
Colin Walters committed
220 221 222
      }

  if (optind+3 != argc)
223
    usage (EXIT_FAILURE);
224

Richard M. Stallman's avatar
Richard M. Stallman committed
225
  running_suid = (getuid () != geteuid ());
226

Richard M. Stallman's avatar
Richard M. Stallman committed
227
  prefix = get_prefix (running_suid, user_prefix);
Colin Walters's avatar
Colin Walters committed
228

Richard M. Stallman's avatar
Richard M. Stallman committed
229
  scorefile = malloc (strlen (prefix) + strlen (argv[optind]) + 2);
Colin Walters's avatar
Colin Walters committed
230
  if (!scorefile)
Richard M. Stallman's avatar
Richard M. Stallman committed
231
    lose_syserr ("Couldn't allocate score file");
Colin Walters's avatar
Colin Walters committed
232

Richard M. Stallman's avatar
Richard M. Stallman committed
233 234 235 236
  strcpy (scorefile, prefix);
  strcat (scorefile, "/");
  strcat (scorefile, argv[optind]);
  newscore = atoi (argv[optind+1]);
Colin Walters's avatar
Colin Walters committed
237
  newdata = argv[optind+2];
Richard M. Stallman's avatar
Richard M. Stallman committed
238
  if (strlen (newdata) > MAX_DATA_LEN)
Colin Walters's avatar
Colin Walters committed
239
    newdata[MAX_DATA_LEN] = '\0';
Colin Walters's avatar
Colin Walters committed
240

241 242
  user_id = get_user_id ();
  if (user_id == NULL)
Richard M. Stallman's avatar
Richard M. Stallman committed
243
    lose_syserr ("Couldn't determine user id");
244

Richard M. Stallman's avatar
Richard M. Stallman committed
245 246
  if (stat (scorefile, &buf) < 0)
    lose_syserr ("Failed to access scores file");
247

Richard M. Stallman's avatar
Richard M. Stallman committed
248 249
  if (lock_file (scorefile, &lockstate) < 0)
    lose_syserr ("Failed to lock scores file");
250

Richard M. Stallman's avatar
Richard M. Stallman committed
251
  if (read_scores (scorefile, &scores, &scorecount) < 0)
Colin Walters's avatar
Colin Walters committed
252
    {
Richard M. Stallman's avatar
Richard M. Stallman committed
253 254
      unlock_file (scorefile, lockstate);
      lose_syserr ("Failed to read scores file");
Colin Walters's avatar
Colin Walters committed
255
    }
Richard M. Stallman's avatar
Richard M. Stallman committed
256
  push_score (&scores, &scorecount, newscore, user_id, newdata);
257
  sort_scores (scores, scorecount, reverse);
Colin Walters's avatar
Colin Walters committed
258 259 260 261 262 263
  /* Limit the number of scores.  If we're using reverse sorting, then
     we should increment the beginning of the array, to skip over the
     *smallest* scores.  Otherwise, we just decrement the number of
     scores, since the smallest will be at the end. */
  if (scorecount > MAX_SCORES)
    scorecount -= (scorecount - MAX_SCORES);
264 265
  if (reverse)
    scores += (scorecount - MAX_SCORES);
Richard M. Stallman's avatar
Richard M. Stallman committed
266
  if (write_scores (scorefile, scores, scorecount) < 0)
Colin Walters's avatar
Colin Walters committed
267
    {
Richard M. Stallman's avatar
Richard M. Stallman committed
268 269
      unlock_file (scorefile, lockstate);
      lose_syserr ("Failed to write scores file");
Colin Walters's avatar
Colin Walters committed
270
    }
Richard M. Stallman's avatar
Richard M. Stallman committed
271
  unlock_file (scorefile, lockstate);
272
  exit (EXIT_SUCCESS);
Colin Walters's avatar
Colin Walters committed
273 274 275
}

int
Richard M. Stallman's avatar
Richard M. Stallman committed
276
read_score (f, score)
277 278
     FILE *f;
     struct score_entry *score;
Colin Walters's avatar
Colin Walters committed
279 280
{
  int c;
Richard M. Stallman's avatar
Richard M. Stallman committed
281
  if (feof (f))
Colin Walters's avatar
Colin Walters committed
282
    return 1;
Richard M. Stallman's avatar
Richard M. Stallman committed
283 284
  while ((c = getc (f)) != EOF
	 && isdigit (c))
Colin Walters's avatar
Colin Walters committed
285 286 287 288
    {
      score->score *= 10;
      score->score += (c-48);
    }
Richard M. Stallman's avatar
Richard M. Stallman committed
289 290
  while ((c = getc (f)) != EOF
	 && isspace (c))
Colin Walters's avatar
Colin Walters committed
291 292 293
    ;
  if (c == EOF)
    return -1;
Richard M. Stallman's avatar
Richard M. Stallman committed
294
  ungetc (c, f);
Colin Walters's avatar
Colin Walters committed
295 296
#ifdef HAVE_GETDELIM
  {
297
    size_t count = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
298
    if (getdelim (&score->username, &count, ' ', f) < 1
Colin Walters's avatar
Colin Walters committed
299 300
	|| score->username == NULL)
      return -1;
301
    /* Trim the space */
Richard M. Stallman's avatar
Richard M. Stallman committed
302
    score->username[strlen (score->username)-1] = '\0';
Colin Walters's avatar
Colin Walters committed
303
  }
Colin Walters's avatar
Colin Walters committed
304 305 306 307
#else
  {
    int unameread = 0;
    int unamelen = 30;
Richard M. Stallman's avatar
Richard M. Stallman committed
308
    char *username = malloc (unamelen);
309 310
    if (!username)
      return -1;
311

Richard M. Stallman's avatar
Richard M. Stallman committed
312 313
    while ((c = getc (f)) != EOF
	   && !isspace (c))
Colin Walters's avatar
Colin Walters committed
314
      {
Colin Walters's avatar
Colin Walters committed
315
	if (unameread >= unamelen-1)
Richard M. Stallman's avatar
Richard M. Stallman committed
316
	  if (!(username = realloc (username, unamelen *= 2)))
Colin Walters's avatar
Colin Walters committed
317
	    return -1;
Colin Walters's avatar
Colin Walters committed
318 319 320
	username[unameread] = c;
	unameread++;
      }
321
    if (c == EOF)
322 323
      return -1;
    username[unameread] = '\0';
Colin Walters's avatar
Colin Walters committed
324 325 326
    score->username = username;
  }
#endif
Colin Walters's avatar
Colin Walters committed
327 328
#ifdef HAVE_GETLINE
  score->data = NULL;
329
  errno = 0;
Colin Walters's avatar
Colin Walters committed
330
  {
331
    size_t len;
Richard M. Stallman's avatar
Richard M. Stallman committed
332
    if (getline (&score->data, &len, f) < 0)
Colin Walters's avatar
Colin Walters committed
333
      return -1;
Richard M. Stallman's avatar
Richard M. Stallman committed
334
    score->data[strlen (score->data)-1] = '\0';
Colin Walters's avatar
Colin Walters committed
335 336
  }
#else
Colin Walters's avatar
Colin Walters committed
337 338 339
  {
    int cur = 0;
    int len = 16;
Richard M. Stallman's avatar
Richard M. Stallman committed
340
    char *buf = malloc (len);
Colin Walters's avatar
Colin Walters committed
341 342
    if (!buf)
      return -1;
Richard M. Stallman's avatar
Richard M. Stallman committed
343
    while ((c = getc (f)) != EOF
344
	   && c != '\n')
Colin Walters's avatar
Colin Walters committed
345 346 347
      {
	if (cur >= len-1)
	  {
Richard M. Stallman's avatar
Richard M. Stallman committed
348
	    if (!(buf = realloc (buf, len *= 2)))
Colin Walters's avatar
Colin Walters committed
349 350 351 352 353 354
	      return -1;
	  }
	buf[cur] = c;
	cur++;
      }
    score->data = buf;
355
    score->data[cur] = '\0';
Colin Walters's avatar
Colin Walters committed
356
  }
Colin Walters's avatar
Colin Walters committed
357 358 359 360 361
#endif
  return 0;
}

int
Richard M. Stallman's avatar
Richard M. Stallman committed
362
read_scores (filename, scores, count)
363 364 365
     const char *filename;
     struct score_entry **scores;
     int *count;
Colin Walters's avatar
Colin Walters committed
366 367 368
{
  int readval, scorecount, cursize;
  struct score_entry *ret;
Richard M. Stallman's avatar
Richard M. Stallman committed
369
  FILE *f = fopen (filename, "r");
370
  if (!f)
Colin Walters's avatar
Colin Walters committed
371 372 373
    return -1;
  scorecount = 0;
  cursize = 16;
374
  ret = (struct score_entry *) malloc (sizeof (struct score_entry) * cursize);
375
  if (!ret)
Colin Walters's avatar
Colin Walters committed
376
    return -1;
Richard M. Stallman's avatar
Richard M. Stallman committed
377
  while ((readval = read_score (f, &ret[scorecount])) == 0)
Colin Walters's avatar
Colin Walters committed
378 379 380 381 382 383 384
    {
      /* We encoutered an error */
      if (readval < 0)
	return -1;
      scorecount++;
      if (scorecount >= cursize)
	{
385 386 387
	  cursize *= 2;
	  ret = (struct score_entry *)
	    realloc (ret, (sizeof (struct score_entry) * cursize));
Colin Walters's avatar
Colin Walters committed
388 389 390 391 392 393 394 395 396 397
	  if (!ret)
	    return -1;
	}
    }
  *count = scorecount;
  *scores = ret;
  return 0;
}

int
Richard M. Stallman's avatar
Richard M. Stallman committed
398
score_compare (a, b)
399 400
     const void *a;
     const void *b;
Colin Walters's avatar
Colin Walters committed
401 402 403 404 405 406 407
{
  const struct score_entry *sa = (const struct score_entry *) a;
  const struct score_entry *sb = (const struct score_entry *) b;
  return (sb->score > sa->score) - (sb->score < sa->score);
}

int
Richard M. Stallman's avatar
Richard M. Stallman committed
408
score_compare_reverse (a, b)
409 410
     const void *a;
     const void *b;
Colin Walters's avatar
Colin Walters committed
411 412 413 414 415 416 417
{
  const struct score_entry *sa = (const struct score_entry *) a;
  const struct score_entry *sb = (const struct score_entry *) b;
  return (sa->score > sb->score) - (sa->score < sb->score);
}

int
418
push_score (scores, count, newscore, username, newdata)
419 420 421 422
     struct score_entry **scores;
     int *count; int newscore;
     char *username;
     char *newdata;
Colin Walters's avatar
Colin Walters committed
423
{
Richard M. Stallman's avatar
Richard M. Stallman committed
424
 struct score_entry *newscores
425 426
   = (struct score_entry *) realloc (*scores,
				     sizeof (struct score_entry) * ((*count) + 1));
Colin Walters's avatar
Colin Walters committed
427 428 429
  if (!newscores)
    return -1;
  newscores[*count].score = newscore;
Colin Walters's avatar
Colin Walters committed
430
  newscores[*count].username = username;
Colin Walters's avatar
Colin Walters committed
431 432 433 434 435
  newscores[*count].data = newdata;
  (*count) += 1;
  *scores = newscores;
  return 0;
}
436

Colin Walters's avatar
Colin Walters committed
437
void
Richard M. Stallman's avatar
Richard M. Stallman committed
438
sort_scores (scores, count, reverse)
439 440
     struct score_entry *scores;
     int count;
441
     int reverse;
Colin Walters's avatar
Colin Walters committed
442
{
Richard M. Stallman's avatar
Richard M. Stallman committed
443
  qsort (scores, count, sizeof (struct score_entry),
Colin Walters's avatar
Colin Walters committed
444 445 446 447
	reverse ? score_compare_reverse : score_compare);
}

int
Richard M. Stallman's avatar
Richard M. Stallman committed
448
write_scores (filename, scores, count)
449 450
     const char *filename;
     const struct score_entry * scores;
451
     int count;
Colin Walters's avatar
Colin Walters committed
452
{
453
  FILE *f;
Colin Walters's avatar
Colin Walters committed
454
  int i;
Richard M. Stallman's avatar
Richard M. Stallman committed
455
  char *tempfile = malloc (strlen (filename) + strlen (".tempXXXXXX") + 1);
Colin Walters's avatar
Colin Walters committed
456 457
  if (!tempfile)
    return -1;
Richard M. Stallman's avatar
Richard M. Stallman committed
458 459
  strcpy (tempfile, filename);
  strcat (tempfile, ".tempXXXXXX");
Colin Walters's avatar
Colin Walters committed
460
#ifdef HAVE_MKSTEMP
Richard M. Stallman's avatar
Richard M. Stallman committed
461
  if (mkstemp (tempfile) < 0
Colin Walters's avatar
Colin Walters committed
462
#else
Richard M. Stallman's avatar
Richard M. Stallman committed
463
  if (mktemp (tempfile) != tempfile
Colin Walters's avatar
Colin Walters committed
464
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
465
      || !(f = fopen (tempfile, "w")))
Colin Walters's avatar
Colin Walters committed
466 467
    return -1;
  for (i = 0; i < count; i++)
Richard M. Stallman's avatar
Richard M. Stallman committed
468
    if (fprintf (f, "%ld %s %s\n", scores[i].score, scores[i].username,
Colin Walters's avatar
Colin Walters committed
469
		scores[i].data) < 0)
Colin Walters's avatar
Colin Walters committed
470
      return -1;
Richard M. Stallman's avatar
Richard M. Stallman committed
471 472
  fclose (f);
  if (rename (tempfile, filename) < 0)
Colin Walters's avatar
Colin Walters committed
473
    return -1;
Richard M. Stallman's avatar
Richard M. Stallman committed
474
  if (chmod (filename, 0644) < 0)
Colin Walters's avatar
Colin Walters committed
475
    return -1;
Colin Walters's avatar
Colin Walters committed
476
  return 0;
Colin Walters's avatar
Colin Walters committed
477
}
478

Colin Walters's avatar
Colin Walters committed
479
int
Richard M. Stallman's avatar
Richard M. Stallman committed
480
lock_file (filename, state)
481 482
  const char *filename;
  void **state;
Colin Walters's avatar
Colin Walters committed
483 484
{
  int fd;
485
  struct stat buf;
Colin Walters's avatar
Colin Walters committed
486 487
  int attempts = 0;
  char *lockext = ".lockfile";
Richard M. Stallman's avatar
Richard M. Stallman committed
488
  char *lockpath = malloc (strlen (filename) + strlen (lockext) + 60);
Colin Walters's avatar
Colin Walters committed
489 490
  if (!lockpath)
    return -1;
Richard M. Stallman's avatar
Richard M. Stallman committed
491 492
  strcpy (lockpath, filename);
  strcat (lockpath, lockext);
Colin Walters's avatar
Colin Walters committed
493 494 495
  *state = lockpath;
 trylock:
  attempts++;
496
  /* If the lock is over an hour old, delete it. */
Richard M. Stallman's avatar
Richard M. Stallman committed
497 498 499
  if (stat (lockpath, &buf) == 0
      && (difftime (buf.st_ctime, time (NULL) > 60*60)))
    unlink (lockpath);
500 501
  fd = open (lockpath, O_CREAT | O_EXCL, 0600);
  if (fd < 0)
Colin Walters's avatar
Colin Walters committed
502 503 504 505 506 507
    {
      if (errno == EEXIST)
	{
	  /* Break the lock; we won't corrupt the file, but we might
	     lose some scores. */
	  if (attempts > MAX_ATTEMPTS)
508
	    {
Richard M. Stallman's avatar
Richard M. Stallman committed
509
	      unlink (lockpath);
510 511
	      attempts = 0;
	    }
Richard M. Stallman's avatar
Richard M. Stallman committed
512
	  sleep ((rand () % 2)+1);
Colin Walters's avatar
Colin Walters committed
513 514 515 516 517
	  goto trylock;
	}
      else
	return -1;
    }
Richard M. Stallman's avatar
Richard M. Stallman committed
518
  close (fd);
Colin Walters's avatar
Colin Walters committed
519 520
  return 0;
}
521

Colin Walters's avatar
Colin Walters committed
522
int
Richard M. Stallman's avatar
Richard M. Stallman committed
523
unlock_file (filename, state)
524 525
  const char *filename;
 void *state;
Colin Walters's avatar
Colin Walters committed
526 527
{
  char *lockpath = (char *) state;
Richard M. Stallman's avatar
Richard M. Stallman committed
528
  int ret = unlink (lockpath);
Colin Walters's avatar
Colin Walters committed
529
  int saved_errno = errno;
Richard M. Stallman's avatar
Richard M. Stallman committed
530
  free (lockpath);
Colin Walters's avatar
Colin Walters committed
531 532 533
  errno = saved_errno;
  return ret;
}
Miles Bader's avatar
Miles Bader committed
534 535 536

/* arch-tag: 2bf5c52e-4beb-463a-954e-c58b9c64736b
   (do not change this comment) */
537 538

/* update-game-score.c ends here */