Add non-interactive mode to disked(8).

This commit is contained in:
Jonas 'Sortie' Termansen 2023-04-23 23:23:49 +02:00
parent b029127df5
commit e9d9eabb6e
2 changed files with 138 additions and 49 deletions

View File

@ -6,7 +6,10 @@
.Nd disk editor
.Sh SYNOPSIS
.Nm disked
.Op Fl in
.Op Fl \-fstab Ns "=" Ns Ar path
.Op Ar device
.Op Ar command ...
.Sh DESCRIPTION
.Nm
is an interactive program that manages partition tables.
@ -19,17 +22,22 @@ supports the Master Boot Record and GUID Partition Table partitioning schemes.
.Pp
.Nm
provides an interactive command line.
Its prompt shows the currently selected device (defaulting to the first device
alphabetically) or
Its prompt shows the currently selected
.Ar device
(defaulting to the first device alphabetically if any) or
.Li (disked)
if none is selected.
If a
.Ar command
is specified on the command line, then that command is run with its arguments
non-interactively instead of reading from the standard input.
.Pp
Commands perform their actions when run rather than waiting for the user to
write out changes.
.Nm
only creates partitions aligned to 1 MiB boundaries whose size is a multiple of
1 MiB.
Unused regions are aligned and those smaller than the alignment are not shown.
.Pp
The options are as follows:
.Bl -tag -width "12345678"
.It Fl \-fstab Ns "=" Ns Ar path
@ -39,6 +47,17 @@ instead of
.Pa /etc/fstab
as
.Xr fstab 5 .
.It Fl i
Run in interactive mode with prompt with line editing.
Commands will ask for missing parameters.
This is the default if the standard input and output are terminals and a
.Ar command
is not specified on the command line.
.It Fl n
Run in non-interactive mode reading commands from the standard input.
Commands will not ask for missing parameters and
.Nm
will immediately exit unsuccessfully if any command fail.
.El
.Pp
The following commands are supported:
@ -78,14 +97,19 @@ Partitions are counted from 1.
Display this manual page if no operands are given, otherwise run
.Xr man 1
with the given command line.
.It Sy mkpart
.It Sy mkpart Ar hole Ar offset Ar length Ar filesystem Ar mountpoint
Create a partition on the current device.
If the partition table has multiple unused regions
.Pq holes ,
.Nm
asks you which one to use.
You need to specify the offset into the hole where the partition is created and
then the length of the partition.
asks you which
.Ar hole
(counting from 1) to use.
You need to specify the
.Ar offset
into the hole where the partition is created and
then the
.Ar length of the partition.
See
.Sx QUANTITIES
below on the possible partition offset and length values.
@ -106,7 +130,9 @@ Use the existing disk data.
.El
.Pp
If you format a mountable filesystem, then you will be asked if you want to
create a mountpoint for the partition, which will be added to
create a
.Ar mountpoint
for the partition, which will be added to
.Xr fstab 5 .
.It Sy mktable Oo mbr "|" gpt Oc
Create a partition table on the current device of the specified type.

View File

@ -240,6 +240,8 @@ static const char* device_name(const char* name)
return name;
}
static bool interactive;
static void display_rows_columns(char* (*format)(void*, size_t, size_t),
void* ctx, size_t rows, size_t columns)
{
@ -395,6 +397,8 @@ static void prompt(char* buffer,
const char* question,
const char* answer)
{
if ( !interactive )
errx(1, "Cannot ask question in non-interactive mode: %s", question);
while ( true )
{
printf("\e[1m");
@ -599,7 +603,6 @@ bool rewrite_finish(struct rewrite* rewr)
return true;
}
static bool interactive;
static bool quitting;
static struct harddisk** hds;
static size_t hds_count;
@ -1132,6 +1135,25 @@ static void switch_device(struct harddisk* hd)
scan_device();
}
static bool lookup_harddisk_by_string(struct harddisk** out,
const char* argv0,
const char* name)
{
for ( size_t i = 0; i < hds_count; i++ )
{
char buf[sizeof(i) * 3];
snprintf(buf, sizeof(buf), "%zu", i);
if ( strcmp(name, hds[i]->path) != 0 &&
strcmp(name, device_name(hds[i]->path)) != 0 &&
strcmp(name, buf) != 0 )
continue;
*out = hds[i];
return true;
}
command_errorx("%s: No such device `%s'", argv0, name);
return false;
}
static bool lookup_partition_by_string(struct partition** out,
const char* argv0,
const char* numstr)
@ -1573,8 +1595,6 @@ static char* display_hole_format(void* ctx, size_t row, size_t column)
static void on_mkpart(size_t argc, char** argv)
{
(void) argc;
(void) argv;
size_t num_holes = 0;
struct device_area* hole = NULL;
for ( size_t i = 0; i < current_areas_count; i++ )
@ -1598,8 +1618,11 @@ static void on_mkpart(size_t argc, char** argv)
char answer[sizeof(size_t) * 3];
while ( true )
{
prompt(answer, sizeof(answer),
"Which hole to create the partition inside?", "1");
if ( 2 <= argc )
strlcpy(answer, argv[1], sizeof(answer));
else
prompt(answer, sizeof(answer),
"Which hole to create the partition inside?", "1");
char* end;
unsigned long num = strtoul(answer, &end, 10);
if ( *end || num_holes < num )
@ -1618,7 +1641,8 @@ static void on_mkpart(size_t argc, char** argv)
}
break;
}
printf("\n");
if ( argc < 2 )
printf("\n");
}
unsigned int slot = 0;
if ( current_pt_type == PARTITION_TABLE_TYPE_MBR && hole->inside_extended )
@ -1678,54 +1702,68 @@ static void on_mkpart(size_t argc, char** argv)
assert(start_str); // TODO: Error handling.
char* length_str = format_bytes_amount((uintmax_t) hole->length);
assert(length_str); // TODO: Error handling.
printf("Creating partition inside hole at %s of length %s (100%%)\n",
start_str, length_str);
if ( argc < 3 )
printf("Creating partition inside hole at %s of length %s (100%%)\n",
start_str, length_str);
free(start_str);
free(length_str);
off_t start;
while ( true )
{
char answer[256];
prompt(answer, sizeof(answer),
"Free space before partition? (42%/15G/...)", "0%");
if ( 3 <= argc )
strlcpy(answer, argv[2], sizeof(answer));
else
prompt(answer, sizeof(answer),
"Free space before partition? (42%/15G/...)", "0%");
if ( !parse_disk_quantity(&start, answer, hole->length) )
{
fprintf(stderr, "Invalid quantity `%s'.\n", answer);
command_errorx("%s: %s: Invalid quantity: %s",
argv[0], device_name(current_hd->path), answer);
continue;
}
if ( start == hole->length )
{
fprintf(stderr, "Answer was all free space, but need space for the "
"partition itself.\n");
command_errorx("%s: %s: Answer was all free space, but need space "
"for the partition itself",
argv[0], device_name(current_hd->path));
continue;
}
break;
}
printf("\n");
if ( argc < 3 )
printf("\n");
off_t max_length = hole->length - start;
off_t length;
length_str = format_bytes_amount((uintmax_t) max_length);
assert(length_str);
printf("Partition size can be at most %s (100%%).\n", length_str);
if ( argc < 4 )
printf("Partition size can be at most %s (100%%).\n", length_str);
free(length_str);
while ( true )
{
char answer[256];
prompt(answer, sizeof(answer),
"Partition size? (42%/15G/...)", "100%");
if ( 4 <= argc )
strlcpy(answer, argv[3], sizeof(answer));
else
prompt(answer, sizeof(answer),
"Partition size? (42%/15G/...)", "100%");
if ( !parse_disk_quantity(&length, answer, max_length) )
{
fprintf(stderr, "Invalid quantity `%s'.\n", answer);
command_errorx("%s: %s: Invalid quantity: %s",
argv[0], device_name(current_hd->path), answer);
continue;
}
if ( length == 0 )
{
fprintf(stderr, "Answer was zero (or rounded down to zero).\n");
command_errorx("%s: %s: Length was zero (or rounded down to zero)",
argv[0], device_name(current_hd->path));
continue;
}
break;
}
printf("\n");
if ( argc < 4 )
printf("\n");
char fstype[256];
while ( true )
{
@ -1736,13 +1774,17 @@ static void on_mkpart(size_t argc, char** argv)
question = "Format a filesystem? (no/ext2/extended)";
else if ( is_gpt )
question = "Format a filesystem? (no/ext2/biosboot)";
prompt(fstype, sizeof(fstype), question, "ext2");
if ( 5 <= argc )
strlcpy(fstype, argv[4], sizeof(fstype));
else
prompt(fstype, sizeof(fstype), question, "ext2");
if ( strcmp(fstype, "no") != 0 &&
strcmp(fstype, "ext2") != 0 &&
(!is_mbr || strcmp(fstype, "extended") != 0) &&
(!is_gpt || strcmp(fstype, "biosboot") != 0) )
{
fprintf(stderr, "Invalid filesystem choice `%s'.\n", fstype);
command_errorx("%s: %s: Invalid filesystem choice: %s",
argv[0], device_name(current_hd->path), fstype);
continue;
}
if ( !strcmp(fstype, "extended") )
@ -1762,27 +1804,27 @@ static void on_mkpart(size_t argc, char** argv)
bool mountable = !strcmp(fstype, "ext2");
while ( mountable )
{
prompt(mountpoint, sizeof(mountpoint),
"Where to mount partition? (mountpoint or 'no')", "no");
if ( 6 <= argc )
strlcpy(mountpoint, argv[5], sizeof(mountpoint));
else
prompt(mountpoint, sizeof(mountpoint),
"Where to mount partition? (mountpoint or 'no')", "no");
if ( !strcmp(mountpoint, "no") )
{
mountpoint[0] = '\0';
break;
}
if ( !strcmp(mountpoint, "mountpoint") )
{
printf("Then answer which mountpoint.\n");
continue;
}
if ( !verify_mountpoint(mountpoint) )
{
fprintf(stderr, "Invalid mountpoint `%s'.\n", fstype);
command_errorx("%s: %s: Invalid mountpoint: %s",
argv[0], device_name(current_hd->path), mountpoint);
continue;
}
simplify_mountpoint(mountpoint);
break;
}
printf("\n");
if ( mountable && argc < 6 )
printf("\n");
size_t renumbered_partitions = 0;
if ( current_pt_type == PARTITION_TABLE_TYPE_MBR && hole->inside_extended )
{
@ -2696,14 +2738,8 @@ static const struct command commands[] =
};
static const size_t commands_count = sizeof(commands) / sizeof(commands[0]);
void execute(char* cmd)
static void execute_argv(int argc, char** argv)
{
size_t argc = 0;
char* argv[256];
split_arguments(cmd, &argc, argv, 255);
if ( argc < 1 )
return;
argv[argc] = NULL;
for ( size_t i = 0; i < commands_count; i++ )
{
if ( strcmp(argv[0], commands[i].name) != 0 )
@ -2738,6 +2774,17 @@ void execute(char* cmd)
"Try `help' for more information.\n", argv[0]);
}
static void execute(char* cmd)
{
size_t argc = 0;
char* argv[256];
split_arguments(cmd, &argc, argv, 255);
if ( argc < 1 )
return;
argv[argc] = NULL;
execute_argv(argc, argv);
}
static void compact_arguments(int* argc, char*** argv)
{
for ( int i = 0; i < *argc; i++ )
@ -2766,6 +2813,8 @@ int main(int argc, char* argv[])
{
setlocale(LC_ALL, "");
bool set_interactive = false;
const char* argv0 = argv[0];
for ( int i = 1; i < argc; i++ )
{
@ -2780,6 +2829,8 @@ int main(int argc, char* argv[])
char c;
while ( (c = *++arg) ) switch ( c )
{
case 'i': interactive = true; set_interactive = true; break;
case 'n': interactive = false; set_interactive = true; break;
default:
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
help(stderr, argv0);
@ -2813,20 +2864,30 @@ int main(int argc, char* argv[])
compact_arguments(&argc, &argv);
interactive = isatty(0) && isatty(1);
if ( !set_interactive )
interactive = isatty(0) && isatty(1) && argc < 3;
if ( !devices_open_all(&hds, &hds_count) )
err(1, "iterating devices");
qsort(hds, hds_count, sizeof(struct harddisk*), harddisk_compare_path);
if ( 1 <= hds_count )
if ( 2 <= argc )
{
struct harddisk* hd;
if ( !lookup_harddisk_by_string(&hd, argv[0], argv[1]) )
exit(1);
switch_device(hd);
}
else if ( 1 <= hds_count )
switch_device(hds[0]);
char* line = NULL;
size_t line_size = 0;
ssize_t line_length;
while ( !quitting )
if ( 3 <= argc )
execute_argv(argc - 2, argv + 2);
else while ( !quitting )
{
if ( interactive )
{
@ -2855,4 +2916,6 @@ int main(int argc, char* argv[])
for ( size_t i = 0; i < hds_count; i++ )
harddisk_close(hds[i]);
free(hds);
return 0;
}