static dsk_boolean resize_mmap (TrivialTableCheckpoint *cp, unsigned min_needed, DskError **error) { unsigned new_size = (min_needed + MMAP_MIN_RESIZE_GROW + MMAP_GRANULARITY - 1) / MMAP_GRANULARITY * MMAP_GRANULARITY; void *mmapped; if (munmap ((void*) cp->mmapped, cp->mmapped_size) < 0) dsk_warning ("error calling munmap(): %s", strerror (errno)); if (ftruncate (cp->fd, new_size) < 0) { dsk_set_error (error, "error expanding file to %u bytes: %s", new_size, strerror (errno)); return DSK_FALSE; } mmapped = mmap (NULL, new_size, PROT_READ|PROT_WRITE, MAP_SHARED, cp->fd, 0); if (mmapped == MAP_FAILED) { dsk_set_error (error, "mmap of %u bytes failed: %s", new_size, strerror (errno)); return DSK_FALSE; } cp->mmapped = mmapped; cp->mmapped_size = new_size; return DSK_TRUE; }
static dsk_boolean handle_incoming_connection (DskOctetListener *listener) { DskHttpServerStream *stream; /* accept connection */ switch (dsk_octet_listener_accept (listener, NULL, &source, &sink, &error)) { case DSK_IO_RESULT_SUCCESS: /* create http-server stream */ stream = dsk_http_server_stream_new (sink, source, &server_stream_options); dsk_assert (stream != NULL); dsk_hook_trap (&stream->request_available, (DskHookFunc) handle_http_stream_request_available, stream, dsk_object_unref); break; case DSK_IO_RESULT_AGAIN: break; case DSK_IO_RESULT_ERROR: dsk_main_exit (1); dsk_warning ("cannot accept a new socket: %s", error->message); dsk_error_unref (error); error = NULL; return DSK_FALSE; case DSK_IO_RESULT_EOF: dsk_assert_not_reached (); } /* invoke this handler when the next incoming connection is available. */ return DSK_TRUE; }
static void table_checkpoint_trivial__destroy(DskTableCheckpoint *checkpoint) { TrivialTableCheckpoint *cp = (TrivialTableCheckpoint *) checkpoint; if (munmap ((void*) cp->mmapped, cp->mmapped_size) < 0) dsk_warning ("error calling munmap(): %s", strerror (errno)); close (cp->fd); dsk_free (cp); }
DskOctetFilter *dsk_bz2lib_decompressor_new (void) { DskBz2libDecompressor *rv = dsk_object_new (&dsk_bz2lib_decompressor_class); int zrv; zrv = BZ2_bzDecompressInit (&rv->bz2lib, DSK_FALSE, DSK_FALSE); if (zrv != BZ_OK) { dsk_warning ("BZ2_bzDecompressInit returned error"); dsk_object_unref (rv); return NULL; } rv->initialized = DSK_TRUE; return DSK_OCTET_FILTER (rv); }
DskOctetFilter *dsk_bz2lib_compressor_new (unsigned level) { DskBz2libCompressor *rv = dsk_object_new (&dsk_bz2lib_compressor_class); int zrv; zrv = BZ2_bzCompressInit (&rv->bz2lib, level, DSK_FALSE, 0); if (zrv != BZ_OK) { dsk_warning ("deflateInit2 returned error: %s", bzrv_to_string (zrv)); dsk_object_unref (rv); return NULL; } rv->initialized = DSK_TRUE; return DSK_OCTET_FILTER (rv); }
void dsk_daemon_maybe_fork (void) { int fork_pipe_fds[2] = {-1,-1}; if (dsk_daemon_do_fork) { int pid; retry_pipe: if (pipe (fork_pipe_fds) < 0) { if (errno == EINTR) goto retry_pipe; dsk_fd_creation_failed (errno); dsk_die ("error creating pipe: %s", strerror (errno)); } retry_daemon_fork: pid = fork (); if (pid < 0) { if (errno == EINTR) goto retry_daemon_fork; dsk_die ("error forking daemon: %s", strerror (errno)); } else if (pid > 0) { /* wait for EOF on pipe */ close (fork_pipe_fds[1]); char buf[1]; for (;;) { int nread = read (fork_pipe_fds[0], buf, 1); if (nread < 0) { if (errno == EINTR) continue; dsk_die ("error reading from semaphore pipe: %s", strerror (errno)); } else if (nread > 0) dsk_die ("somehow got data on semaphore pipe: %s:%u", __FILE__, __LINE__); else break; } _exit (0); } else { /* child process: continue as the non-forking case. */ close (fork_pipe_fds[0]); setsid (); } } int pid_file_fd = -1; if (dsk_daemon_pid_filename) { dsk_boolean must_truncate = DSK_FALSE; dsk_boolean made_dir = DSK_FALSE; retry_outer_pid_file_open: if ((pid_file_fd=open (dsk_daemon_pid_filename, O_CREAT|O_EXCL|O_WRONLY, 0666)) < 0) { if (errno == EINTR) goto retry_outer_pid_file_open; else if (errno == EEXIST) { /* open / lock-nonblocking / rewrite we get lock */ retry_inner_pid_file_open: if ((pid_file_fd=open (dsk_daemon_pid_filename, O_WRONLY, 0666)) < 0) { if (errno == EINTR) goto retry_inner_pid_file_open; dsk_die ("daemonize: error opening lock file %s: %s", dsk_daemon_pid_filename, strerror (errno)); } must_truncate = DSK_TRUE; } else if (errno == ENOENT && !made_dir) { /* make directories, retry */ char *slash = strrchr (dsk_daemon_pid_filename, '/'); if (slash == NULL) dsk_die ("daemonize: error creating %s: no such file or dir (cwd does not exist?)", dsk_daemon_pid_filename); char *dir = dsk_strdup_slice (dsk_daemon_pid_filename, slash); DskError *error = NULL; if (!dsk_mkdir_recursive (dir, 0777, &error)) dsk_die ("error making directory %s: %s", dir, error->message); dsk_free (dir); made_dir = DSK_TRUE; goto retry_outer_pid_file_open; } else { dsk_fd_creation_failed (errno); dsk_die ("daemonize: error creating PID file %s: %s", dsk_daemon_pid_filename, strerror (errno)); } } retry_flock: if (flock (pid_file_fd, LOCK_EX|LOCK_NB) < 0) { if (errno == EINTR) goto retry_flock; if (errno == EWOULDBLOCK) { /* TODO: print PID */ dsk_die ("daemonize: process already running"); } dsk_die ("daemonize: error locking: %s", strerror (errno)); } if (must_truncate) { ftruncate (pid_file_fd, 0); } char buf[32]; snprintf (buf, sizeof (buf), "%u\n", (unsigned)getpid ()); unsigned len = strlen (buf); unsigned written = 0; while (written < len) { int write_rv = write (pid_file_fd, buf + written, len - written); if (write_rv < 0) { if (errno == EINTR) continue; dsk_die ("error writing pid file %s", dsk_daemon_pid_filename); } written += write_rv; } } if (fork_pipe_fds[1] != -1) { close (fork_pipe_fds[1]); } if (dsk_daemon_watchdog) { int alert_pid = 0; unsigned last_alert_time = 0; for (;;) { /* NOTE: must never die, i guess */ int pid; int status; retry_watchdog_fork: pid = fork (); if (pid < 0) { if (errno == EINTR) goto retry_watchdog_fork; dsk_die ("error forking watchdogged process: %s", strerror (errno)); } else if (pid == 0) { if (pid_file_fd >= 0) close (pid_file_fd); maybe_redirect_stdouterr (); add_maybe_redirect_timer (); return; } maybe_redirect_stdouterr (); char time_str[TIME_STR_LENGTH]; make_time_str (time_str); fprintf (stderr, "%s: watchdog: forked process %u\n", time_str, (unsigned) pid); retry_waitpid: if (waitpid (pid, &status, 0) < 0) { if (errno == EINTR) goto retry_waitpid; dsk_die ("error running waitpid %u: %s", pid, strerror (errno)); } maybe_redirect_stdouterr (); make_time_str (time_str); if (WIFEXITED (status)) fprintf (stderr, "%s: watchdog: process %u exited with status %u\n", time_str, pid, WEXITSTATUS (status)); else if (WIFSIGNALED (status)) fprintf (stderr, "%s: watchdog: process %u killed by signal %u%s\n", time_str, pid, WTERMSIG (status), WCOREDUMP (status) ? " [core dumped]" : ""); else fprintf (stderr, "%s: watchdog: process %u died in some creative way\n", time_str, pid); /* configurable? */ sleep (1); /* send alert */ if (dsk_daemon_alert_script) { int time_delta = time (NULL) - last_alert_time; unsigned clamped_delta = time_delta < 0 ? 0 : time_delta; if (alert_pid > 0) { int rv = waitpid (alert_pid, &status, WNOHANG); if (rv < 0) { if (errno == EINTR) goto retry_waitpid; else dsk_die ("error waiting for alert process"); } else if (rv == 0) { /* process has not terminated */ } else { /* process terminated (ignore status?) */ alert_pid = 0; } } if (alert_pid == 0 && clamped_delta > dsk_daemon_alert_interval) { retry_alert_fork: alert_pid = fork (); if (alert_pid < 0) { if (errno == EINTR) goto retry_alert_fork; dsk_warning ("error forking alert process: %s", strerror (errno)); alert_pid = 0; } else if (alert_pid == 0) { execl ("/bin/sh", "/bin/sh", "-c", dsk_daemon_alert_script, NULL); _exit (127); } else dsk_daemon_n_alerts_suppressed = 0; } else ++dsk_daemon_n_alerts_suppressed; } } } }
static void test_various_write_seek_1 (const char *name, unsigned n_entries, TestEntry *entries, unsigned n_negative, TestEntry *neg_entries) { DskError *error = NULL; unsigned i; DskTableFileInterface *iface = &dsk_table_file_interface_trivial; DskTableFileWriter *writer; DskTableFileSeeker *seeker; if (cmdline_verbose) fprintf (stderr, "running dataset %s [%u]\n", name, n_entries); else fprintf (stderr, "."); writer = iface->new_writer (iface, location, "base", &error); if (writer == NULL) dsk_die ("%s", error->message); for (i = 0; i < n_entries; i++) { TestEntry *e = entries + i; if (!writer->write (writer, strlen (e->key), (uint8_t*) e->key, strlen (e->value), (uint8_t*) e->value, &error)) dsk_die ("error writing: %s", error->message); } if (!writer->close (writer, &error)) dsk_die ("error closing writer: %s", error->message); writer->destroy (writer); /* --- now test seeker --- */ /* pick the step size. */ { static unsigned prime_table[] = { 29, 31, 37, 41, 43, 47, 53, 59, 61, 67 }; unsigned *p_ptr = prime_table + DSK_N_ELEMENTS (prime_table) - 1; unsigned max_test, n_test, test_i, step; while (n_entries % *p_ptr == 0) p_ptr--; step = *p_ptr % n_entries; /* create seeker */ seeker = iface->new_seeker (iface, location, "base", &error); if (seeker == NULL) dsk_die ("error creating seeker from newly finished writer: %s", error->message); max_test = cmdline_slow ? 100000 : 1000; n_test = DSK_MIN (n_entries, max_test); test_i = step; for (i = 0; i < n_test; i++) { unsigned key_len, value_len; const uint8_t *key_data, *value_data; /* do the seek */ if (!seeker->find (seeker, str_test_func, (void*) entries[test_i].key, DSK_TABLE_FILE_FIND_ANY, &key_len, &key_data, &value_len, &value_data, &error)) { if (error) dsk_die ("error doing find that should have succeeded: %s", error->message); else dsk_die ("not found doing find that should have succeeded"); } #if 0 dsk_warning ("test=%s, got result %.*s", entries[test_i].key, (int) key_len, key_data); #endif dsk_assert (key_len == strlen (entries[test_i].key)); dsk_assert (value_len == strlen (entries[test_i].value)); dsk_assert (memcmp (key_data, entries[test_i].key, key_len) == 0); dsk_assert (memcmp (value_data, entries[test_i].value, value_len) == 0); /* advance test_i */ test_i += step; if (test_i >= n_entries) test_i -= n_entries; } /* do negative tests */ for (i = 0; i < n_negative; i++) { unsigned key_len, value_len; const uint8_t *key_data, *value_data; /* do the seek */ if (!seeker->find (seeker, str_test_func, (void*) neg_entries[i].key, DSK_TABLE_FILE_FIND_ANY, &key_len, &key_data, &value_len, &value_data, &error)) { if (error) dsk_die ("error doing find that should have returned nothing: %s", error->message); } else if (key_len == strlen (neg_entries[i].key) && memcmp (key_data, neg_entries[i].key, key_len) == 0) { dsk_die ("found result when none expected"); } } seeker->destroy (seeker); } }
static Game * create_game (const char *name, unsigned width, unsigned height) { Game *game = dsk_malloc (sizeof (Game)); unsigned usize; unsigned i; game->name = dsk_strdup (name); game->next_game = all_games; all_games = game; game->universe_width = width; game->universe_height = height; usize = width * height; game->h_walls = generate_ones (usize); game->v_walls = generate_ones (usize); for (i = 0; i < N_OBJECT_TYPES; i++) game->objects[i] = NULL; game->generators = NULL; game->cells = dsk_malloc0 (sizeof (Cell) * width * height); game->latest_update = 0; game->wrap = DSK_TRUE; game->diag_bullets_bounce = DSK_TRUE; game->bullet_kills_player = DSK_TRUE; game->bullet_kills_generator = DSK_TRUE; game->pending_updates = NULL; /* Generate with Modified Kruskals Algorithm, see * http://en.wikipedia.org/wiki/Maze_generation_algorithm */ TmpWall *tmp_walls = dsk_malloc (sizeof (TmpWall) * usize * 2); TmpSetInfo *sets = dsk_malloc (sizeof (TmpSetInfo) * usize); /* connect the walls together in random order */ unsigned *scramble; scramble = dsk_malloc (sizeof (unsigned) * usize * 2); for (i = 0; i < usize * 2; i++) scramble[i] = i; for (i = 0; i < usize * 2; i++) swap_ints (scramble + random_int_range (usize * 2), scramble + random_int_range (usize * 2)); TmpWall *wall_list = NULL; for (i = 0; i < usize * 2; i++) { unsigned e = scramble[i]; unsigned h = e % 2; unsigned x = (e / 2) % width; unsigned y = e / (width * 2); if (!game->wrap) { if ((h && y == 0) || (!h && x == 0)) continue; } tmp_walls[e].prev = NULL; tmp_walls[e].next = wall_list; if (wall_list) wall_list->prev = tmp_walls + e; wall_list = tmp_walls + e; } for (i = 0; i < usize; i++) { sets[i].set_number = i; sets[i].next_in_set = sets + i; } while (wall_list != NULL) { /* Invariants: - the sets are in a ring by set number. - The wall_list only consists of walls that separate distinct sets. */ /* remove wall */ unsigned e = wall_list - tmp_walls; unsigned h = e % 2; unsigned x = (e / 2) % width; unsigned y = e / (width * 2); TmpSetInfo *si = sets + e / 2; TmpSetInfo *osi; if (h) { if (y == 0) osi = si + (height - 1) * width; else osi = si - width; game->h_walls[x + y * width] = 0; } else { if (x == 0) osi = si + width - 1; else osi = si - 1; game->v_walls[x + y * width] = 0; } dsk_assert (osi->set_number != si->set_number); TmpSetInfo *kring = osi->set_number < si->set_number ? osi : si; /* ring to keep */ TmpSetInfo *dring = osi->set_number < si->set_number ? si : osi; /* ring to change */ TmpSetInfo *dring_start = dring; /* combine sets (removing any walls that no longer separate different sets from the list of walls to remove) */ unsigned set = kring->set_number; do { unsigned x = (dring - sets) % width; unsigned y = (dring - sets) / width; int wall_idx; dring->set_number = set; #if 0 if (wall_list) { dsk_assert (wall_list->prev == NULL); TmpWall *t; for (t = wall_list; t; t = t->next) if (t->next) dsk_assert (t->next->prev == t); } #endif /* Maybe remove left wall from candidate set of walls. */ wall_idx = -1; if (x > 0 && (dring-1)->set_number == set) wall_idx = 2 * (x + y * width); else if (x == 0 && game->wrap && (dring+width-1)->set_number == set) wall_idx = 2 * (x + y * width); if (wall_idx >= 0) remove_tmp_wall (tmp_walls, wall_idx, &wall_list); /* Maybe remove right wall from candidate set of walls. */ wall_idx = -1; if (x < width - 1 && (dring+1)->set_number == set) wall_idx = 2 * ((x+1) + y * width); else if (x == width - 1 && game->wrap && (dring-width+1)->set_number == set) wall_idx = 2 * (0 + y * width); if (wall_idx >= 0) remove_tmp_wall (tmp_walls, wall_idx, &wall_list); /* Maybe remove top wall from candidate set of walls. */ wall_idx = -1; if (y > 0 && (dring-width)->set_number == dring->set_number) wall_idx = 2 * (x + y * width) + 1; else if (y == 0 && game->wrap && (dring+width*(height-1))->set_number == set) wall_idx = 2 * (x + y * width) + 1; if (wall_idx >= 0) remove_tmp_wall (tmp_walls, wall_idx, &wall_list); /* Maybe remove bottom wall from candidate set of walls. */ wall_idx = -1; if (y < height - 1 && (dring+width)->set_number == set) wall_idx = 2 * (x + (y+1) * width) + 1; else if (y == height - 1 && game->wrap && (dring-(height-1)*width)->set_number == set) wall_idx = 2 * (x + 0 * width) + 1; if (wall_idx >= 0) remove_tmp_wall (tmp_walls, wall_idx, &wall_list); dring = dring->next_in_set; } while (dring != dring_start); /* Merge the rings */ TmpSetInfo *old_dring_next = dring->next_in_set; dring->next_in_set = kring->next_in_set; kring->next_in_set = old_dring_next; } dsk_free (tmp_walls); dsk_free (sets); dsk_free (scramble); /* generate generators */ unsigned n_generators = 12 + rand () % 6; dsk_warning ("%u generators", n_generators); i = 0; while (i < n_generators) { unsigned idx = random_int_range (usize); Cell *cell = game->cells + idx; if (cell->generator == NULL) { cell->generator = dsk_malloc (sizeof (Generator)); cell->generator->game = game; cell->generator->x = (idx % width) * CELL_SIZE + CELL_SIZE/2; cell->generator->y = (idx / width) * CELL_SIZE + CELL_SIZE/2; dsk_warning ("created generator at %u,%u",cell->generator->x ,cell->generator->y); cell->generator->generator_prob = 0.01; cell->generator->next_in_game = game->generators; cell->generator->prev_in_game = NULL; if (game->generators) game->generators->prev_in_game = cell->generator; game->generators = cell->generator; i++; } } game->timer = dsk_main_add_timer_millis (update_period_msecs, (DskTimerFunc) game_update_timer_callback, game); return game; }
static DskTableCheckpoint * table_checkpoint_trivial__open (DskTableCheckpointInterface *iface, DskDir *dir, const char *basename, unsigned *cp_data_len_out, uint8_t **cp_data_out, DskTableCheckpointReplayFunc func, void *func_data, DskError **error) { int fd = -1; struct stat stat_buf; void *mmapped = NULL; TrivialTableCheckpoint *rv; unsigned version, cp_data_len; unsigned at; DSK_UNUSED (iface); /* open fd */ fd = dsk_dir_openfd (dir, basename, DSK_DIR_OPENFD_WRITABLE, 0, error); if (fd < 0) return NULL; /* fstat */ if (fstat (fd, &stat_buf) < 0) { dsk_set_error (error, "fstat of %s failed: %s", basename, strerror (errno)); goto error_cleanup; } /* mmap */ mmapped = mmap (NULL, stat_buf.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (mmapped == MAP_FAILED) { dsk_set_error (error, "mmap of %u bytes (file %s) failed: %s", (unsigned) stat_buf.st_size, basename, strerror (errno)); goto error_cleanup; } /* check format */ if (((uint32_t*)mmapped)[0] != UINT32_TO_LE (TRIVIAL_CP_MAGIC)) { dsk_set_error (error, "checkpoint file %s has bad magic", basename); goto error_cleanup; } version = UINT32_FROM_LE (((uint32_t*)mmapped)[1]); if (version != 1) { dsk_set_error (error, "checkpoint file %s has bad version %u", basename, version); goto error_cleanup; } cp_data_len = UINT32_FROM_LE (((uint32_t*)mmapped)[2]); /* replay */ at = 12 + (cp_data_len + 3) / 4 * 4; if (at + 4 > stat_buf.st_size) { dsk_set_error (error, "checkpoint data length (%u) too big for file's length (%u)", cp_data_len, (unsigned) stat_buf.st_size); goto error_cleanup; } while (* (uint32_t*) (mmapped+at) != 0xffffffff) { uint32_t key_len, value_len; uint32_t kv_len, kvp_len; if (at + 8 > stat_buf.st_size) { dsk_set_error (error, "checkpoint entry header too long"); goto error_cleanup; } key_len = UINT32_FROM_LE (((uint32_t *) (mmapped+at))[0]); value_len = UINT32_FROM_LE (((uint32_t *) (mmapped+at))[1]); kv_len = key_len + value_len; if (kv_len < key_len) { dsk_set_error (error, "key+value length greater than 4G"); goto error_cleanup; } kvp_len = (kv_len + 3) / 4 * 4; if (at + 8 + kvp_len + 4 > stat_buf.st_size) { dsk_set_error (error, "checkpoint entry data too long"); goto error_cleanup; } if (!func (key_len, mmapped + at + 8, value_len, mmapped + at + 8 + key_len, func_data, error)) { if (error && !*error) dsk_set_error (error, "replay handler returned false but didn't set error"); goto error_cleanup; } at += 8 + kvp_len; } /* copy cp_data */ if (cp_data_len_out != NULL) *cp_data_len_out = cp_data_len; if (cp_data_out != NULL) { *cp_data_out = dsk_malloc (cp_data_len); memcpy (*cp_data_out, mmapped + 12, cp_data_len); } rv = DSK_NEW (TrivialTableCheckpoint); rv->base = table_checkpoint_trivial__vfuncs; rv->fd = fd; rv->mmapped = mmapped; rv->mmapped_size = stat_buf.st_size; rv->cur_size = at; return &rv->base; error_cleanup: if (mmapped != NULL && munmap ((void*) mmapped, stat_buf.st_size) < 0) dsk_warning ("error calling munmap(): %s", strerror (errno)); if (fd >= 0) close (fd); return NULL; }