make sure we gracefully clean up and only exit at safe points.
For fsck, we pass the SIGINT/SIGTERM signal to the child processes,
so they can do their own cleanup.
+2002-07-21 Theodore Ts'o <tytso@mit.edu>
+
+ * e2fsck.8.in: Document new exit code FSCK_CANCELED
+
+ * unix.c (PRS, signal_cancel): Capture SIGINT and SIGTERM signals
+ and set a flag in the e2fsck context indicating that
+ cancellation has been requested, so that e2fsck will exit
+ only at safe points.
+ (main): Change the exit handling so that if a cancellation
+ is requested, return FSCK_CANCELED (a new exit code 32).
+ e2fsck can now return more than one exit code as part of a
+ bitmask (as had been documented in the man page).
+
+ * pass2.c (e2fsck_pass2, check_dir_block), pass3.c (e2fsck_pass3),
+ pass4.c (e2fsck_pass4): Check to see if a cancellation was
+ requested, and abort processing if necessary.
+
2002-07-19 Theodore Ts'o <tytso@mit.edu>
* rehash.c, Makefile.in: New file which rewrites directories using
.br
\ 2\ \-\ File system errors corrected, system should
.br
-\ \ \ \ be rebooted if file system was mounted
+\ \ \ \ be rebooted
.br
\ 4\ \-\ File system errors left uncorrected
.br
.br
\ 16\ \-\ Usage or syntax error
.br
+\ 32\ \-\ E2fsck canceled by user request
+.br
\ 128\ \-\ Shared library error
.br
.SH SIGNALS
#define FSCK_UNCORRECTED 4 /* File system errors left uncorrected */
#define FSCK_ERROR 8 /* Operational error */
#define FSCK_USAGE 16 /* Usage or syntax error */
+#define FSCK_CANCELED 32 /* Aborted with a signal or ^C */
#define FSCK_LIBRARY 128 /* Shared library error */
/*
#ifdef ENABLE_HTREE
for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) {
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return;
if (dx_dir->numblocks == 0)
continue;
clear_problem_context(&pctx);
buf = cd->buf;
ctx = cd->ctx;
- if (ctx->progress)
- if ((ctx->progress)(ctx, 2, cd->count++, cd->max))
- return DIRENT_ABORT;
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return DIRENT_ABORT;
+
+ if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
+ return DIRENT_ABORT;
/*
* Make sure the inode is still in use (could have been
goto abort_exit;
for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) {
- if (ctx->progress)
- if ((ctx->progress)(ctx, 3, count++, maxdirs))
- goto abort_exit;
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ goto abort_exit;
+ if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs))
+ goto abort_exit;
if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino))
if (check_directory(ctx, dir, &pctx))
goto abort_exit;
return;
for (i=1; i <= fs->super->s_inodes_count; i++) {
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return;
if ((i % fs->super->s_inodes_per_group) == 0) {
group++;
if (ctx->progress)
e2fsck_clear_progbar(ctx);
ctx->progress = 0;
}
+
+static void signal_cancel(int sig)
+{
+ e2fsck_t ctx = e2fsck_global_ctx;
+
+ if (!ctx)
+ exit(FSCK_CANCELED);
+
+ ctx->flags |= E2F_FLAG_CANCEL;
+}
#endif
static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
sigaction(SIGUSR1, &sa, 0);
sa.sa_handler = signal_progress_off;
sigaction(SIGUSR2, &sa, 0);
+ sa.sa_handler = signal_cancel;
+ sigaction(SIGINT, &sa, 0);
+ sigaction(SIGTERM, &sa, 0);
#endif
/* Update our PATH to include /sbin if we need to run badblocks */
ext2fs_close(fs);
goto restart;
}
- if (run_result & E2F_FLAG_SIGNAL_MASK)
- fatal_error(ctx, 0);
- if (run_result & E2F_FLAG_CANCEL)
- ext2fs_unmark_valid(fs);
+ if (run_result & E2F_FLAG_CANCEL) {
+ printf(_("%s: e2fsck canceled.\n"), ctx->device_name ?
+ ctx->device_name : ctx->filesystem_name);
+ exit_value |= FSCK_CANCELED;
+ }
+ if (run_result & E2F_FLAG_ABORT)
+ fatal_error(ctx, _("aborted"));
#ifdef MTRACE
mtrace_print("Cleanup");
#endif
if (ext2fs_test_changed(fs)) {
- exit_value = FSCK_NONDESTRUCT;
+ exit_value |= FSCK_NONDESTRUCT;
if (!(ctx->options & E2F_OPT_PREEN))
printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
ctx->device_name);
if (root_filesystem && !read_only_root) {
printf(_("%s: ***** REBOOT LINUX *****\n"),
ctx->device_name);
- exit_value = FSCK_REBOOT;
+ exit_value |= FSCK_REBOOT;
}
}
if (ext2fs_test_valid(fs))
else {
printf(_("\n%s: ********** WARNING: Filesystem still has "
"errors **********\n\n"), ctx->device_name);
- exit_value = FSCK_UNCORRECTED;
+ exit_value |= FSCK_UNCORRECTED;
+ exit_value &= ~FSCK_NONDESTRUCT;
}
if (!(ctx->options & E2F_OPT_READONLY)) {
if (ext2fs_test_valid(fs)) {
if (!(sb->s_state & EXT2_VALID_FS))
- exit_value = FSCK_NONDESTRUCT;
+ exit_value |= FSCK_NONDESTRUCT;
sb->s_state = EXT2_VALID_FS;
} else
sb->s_state &= ~EXT2_VALID_FS;
sb->s_lastcheck = time(NULL);
ext2fs_mark_super_dirty(fs);
}
- show_stats(ctx);
+ if (exit_value & FSCK_CANCELED)
+ exit_value &= ~FSCK_NONDESTRUCT;
+ else
+ show_stats(ctx);
e2fsck_write_bitmaps(ctx);
+2002-07-21 Theodore Ts'o <tytso@mit.edu>
+
+ * fsck.8.in: Document new fsck exit code, FSCK_CANCELED.
+
+ * fsck.c: Trap SIGINT and SIGTERM, and relay it to all of the
+ child fsck processes.
+
2002-07-14 Theodore Ts'o <tytso@mit.edu>
* mke2fs.c (zap_sector): Clear the buffer *after* testing for the
.br
\ 16\ \-\ Usage or syntax error
.br
+\ 32\ \-\ Fsck canceled by user request
+.br
\ 128\ \-\ Shared library error
.br
The exit code returned when all file systems are checked using the
int force_all_parallel = 0;
int num_running = 0;
int max_running = 0;
+volatile int cancel_requested = 0;
+int kill_sent = 0;
char *progname;
char *fstype = NULL;
struct fs_info *filesys_info;
}
/*
+ * Send a signal to all outstanding fsck child processes
+ */
+static int kill_all(int signal)
+{
+ struct fsck_instance *inst;
+ int n = 0;
+
+ for (inst = instance_list; inst; inst = inst->next) {
+ if (inst->flags & FLAG_DONE)
+ continue;
+ kill(inst->pid, signal);
+ n++;
+ }
+ return n;
+}
+
+/*
* Wait for one child process to exit; when it does, unlink it from
* the list of executing child processes, and return it.
*/
do {
pid = waitpid(-1, &status, flags);
+ if (cancel_requested && !kill_sent) {
+ kill_all(SIGTERM);
+ kill_sent++;
+ }
if ((pid == 0) && (flags & WNOHANG))
return NULL;
if (pid < 0) {
pass_done = 1;
for (fs = filesys_info; fs; fs = fs->next) {
+ if (cancel_requested)
+ break;
if (fs->flags & FLAG_DONE)
continue;
/*
break;
}
}
+ if (cancel_requested)
+ break;
if (verbose > 1)
printf(_("--waiting-- (pass %d)\n"), passno);
status |= wait_all(pass_done ? 0 : WNOHANG);
} else
not_done_yet++;
}
+ if (cancel_requested && !kill_sent) {
+ kill_all(SIGTERM);
+ kill_sent++;
+ }
status |= wait_all(0);
return status;
}
exit(EXIT_USAGE);
}
+#ifdef HAVE_SIGNAL_H
+static void signal_cancel(int sig)
+{
+ cancel_requested++;
+}
+#endif
+
static void PRS(int argc, char *argv[])
{
int i, j;
char options[128];
int opt = 0;
int opts_for_fsck = 0;
+#ifdef HAVE_SIGNAL_H
+ struct sigaction sa;
+
+ /*
+ * Set up signal action
+ */
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_handler = signal_cancel;
+ sigaction(SIGINT, &sa, 0);
+ sigaction(SIGTERM, &sa, 0);
+#endif
num_devices = 0;
num_args = 0;
exit(EXIT_ERROR);
}
for (i = 0 ; i < num_devices; i++) {
+ if (cancel_requested) {
+ if (!kill_sent) {
+ kill_all(SIGTERM);
+ kill_sent++;
+ }
+ break;
+ }
fsck_device(devices[i], interactive);
if (serialize || (num_running >= max_running)) {
struct fsck_instance *inst;