2012-03-11 10:57:13 -04:00
|
|
|
/*******************************************************************************
|
|
|
|
|
2015-03-21 12:28:52 -04:00
|
|
|
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014, 2015.
|
2012-03-11 10:57:13 -04:00
|
|
|
|
2013-07-10 09:26:01 -04:00
|
|
|
This program is free software: you can redistribute it and/or modify it
|
|
|
|
under the terms of the GNU General Public License as published by the Free
|
|
|
|
Software Foundation, either version 3 of the License, or (at your option)
|
|
|
|
any later version.
|
2012-03-11 10:57:13 -04:00
|
|
|
|
2013-07-10 09:26:01 -04:00
|
|
|
This program 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.
|
2012-03-11 10:57:13 -04:00
|
|
|
|
2013-07-10 09:26:01 -04:00
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
|
|
this program. If not, see <http://www.gnu.org/licenses/>.
|
2012-03-11 10:57:13 -04:00
|
|
|
|
2014-10-04 11:34:51 -04:00
|
|
|
init.c++
|
2014-09-30 16:59:34 -04:00
|
|
|
Start the operating system.
|
2012-03-11 10:57:13 -04:00
|
|
|
|
|
|
|
*******************************************************************************/
|
|
|
|
|
2013-06-01 07:52:36 -04:00
|
|
|
#define __STDC_CONSTANT_MACROS
|
|
|
|
#define __STDC_LIMIT_MACROS
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
#include <sys/display.h>
|
2014-09-30 16:59:34 -04:00
|
|
|
#include <sys/mount.h>
|
2012-08-07 18:19:44 -04:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
2011-11-06 17:51:02 -05:00
|
|
|
#include <sys/wait.h>
|
2012-08-07 18:19:44 -04:00
|
|
|
|
2013-06-01 07:52:36 -04:00
|
|
|
#include <assert.h>
|
2015-07-23 20:24:49 -04:00
|
|
|
#include <ctype.h>
|
2013-06-01 07:52:36 -04:00
|
|
|
#include <dirent.h>
|
2011-11-22 11:26:47 -05:00
|
|
|
#include <errno.h>
|
2011-11-26 05:00:45 -05:00
|
|
|
#include <error.h>
|
2011-11-24 04:26:36 -05:00
|
|
|
#include <fcntl.h>
|
2015-07-23 20:24:49 -04:00
|
|
|
#include <fstab.h>
|
2013-10-01 17:13:01 -04:00
|
|
|
#include <grp.h>
|
2015-07-23 20:24:49 -04:00
|
|
|
#include <locale.h>
|
2013-10-01 17:13:01 -04:00
|
|
|
#include <pwd.h>
|
2013-06-01 07:52:36 -04:00
|
|
|
#include <signal.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2015-07-23 20:24:49 -04:00
|
|
|
#include <termios.h>
|
2014-06-11 19:00:19 -04:00
|
|
|
#include <time.h>
|
|
|
|
#include <timespec.h>
|
2012-01-22 17:46:41 -05:00
|
|
|
#include <unistd.h>
|
2011-09-21 14:52:29 -04:00
|
|
|
|
2013-06-01 07:52:36 -04:00
|
|
|
#include <fsmarshall.h>
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
#include <mount/blockdevice.h>
|
|
|
|
#include <mount/devices.h>
|
|
|
|
#include <mount/filesystem.h>
|
|
|
|
#include <mount/harddisk.h>
|
|
|
|
#include <mount/partition.h>
|
|
|
|
#include <mount/uuid.h>
|
|
|
|
|
|
|
|
static char* read_single_line(FILE* fp)
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
|
|
|
char* ret = NULL;
|
|
|
|
size_t ret_size = 0;
|
|
|
|
ssize_t ret_length = getline(&ret, &ret_size, fp);
|
|
|
|
if ( ret_length < 0 )
|
2015-05-11 09:49:54 -04:00
|
|
|
{
|
|
|
|
free(ret);
|
2013-06-01 07:52:36 -04:00
|
|
|
return NULL;
|
2015-05-11 09:49:54 -04:00
|
|
|
}
|
2013-06-01 07:52:36 -04:00
|
|
|
if ( ret_length && ret[ret_length-1] == '\n' )
|
|
|
|
ret[--ret_length] = '\0';
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
static char* join_paths(const char* a, const char* b)
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
|
|
|
size_t a_len = strlen(a);
|
|
|
|
bool has_slash = (a_len && a[a_len-1] == '/') || b[0] == '/';
|
2015-07-23 20:24:49 -04:00
|
|
|
char* result;
|
|
|
|
if ( (has_slash && asprintf(&result, "%s%s", a, b) < 0) ||
|
|
|
|
(!has_slash && asprintf(&result, "%s/%s", a, b) < 0) )
|
|
|
|
return NULL;
|
|
|
|
return result;
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
__attribute__((noreturn))
|
|
|
|
__attribute__((format(printf, 1, 2)))
|
|
|
|
static void fatal(const char* format, ...)
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
fprintf(stderr, "%s: fatal: ", program_invocation_name);
|
|
|
|
vfprintf(stderr, format, ap);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
fflush(stderr);
|
|
|
|
va_end(ap);
|
|
|
|
_exit(2);
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
__attribute__((format(printf, 1, 2)))
|
|
|
|
static void warning(const char* format, ...)
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
fprintf(stderr, "%s: warning: ", program_invocation_name);
|
|
|
|
vfprintf(stderr, format, ap);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
fflush(stderr);
|
|
|
|
va_end(ap);
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
__attribute__((format(printf, 1, 2)))
|
|
|
|
static void note(const char* format, ...)
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
fprintf(stderr, "%s: ", program_invocation_name);
|
|
|
|
vfprintf(stderr, format, ap);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
fflush(stderr);
|
|
|
|
va_end(ap);
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
struct harddisk** hds = NULL;
|
|
|
|
size_t hds_used = 0;
|
|
|
|
size_t hds_length = 0;
|
|
|
|
|
|
|
|
void prepare_filesystem(const char* path, struct blockdevice* bdev)
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
enum filesystem_error fserr = blockdevice_inspect_filesystem(&bdev->fs, bdev);
|
|
|
|
if ( fserr == FILESYSTEM_ERROR_ABSENT ||
|
|
|
|
fserr == FILESYSTEM_ERROR_UNRECOGNIZED )
|
|
|
|
return;
|
|
|
|
if ( fserr != FILESYSTEM_ERROR_NONE )
|
|
|
|
return warning("probing: %s: %s", path, filesystem_error_string(fserr));
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
bool prepare_block_device(void* ctx, const char* path)
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
(void) ctx;
|
|
|
|
struct harddisk* hd = harddisk_openat(AT_FDCWD, path, O_RDONLY);
|
|
|
|
if ( !hd )
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
int true_errno = errno;
|
|
|
|
struct stat st;
|
|
|
|
if ( lstat(path, &st) == 0 && !S_ISBLK(st.st_mode) )
|
|
|
|
return true;
|
|
|
|
errno = true_errno;
|
|
|
|
fatal("%s: %m", path);
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( !harddisk_inspect_blockdevice(hd) )
|
2013-10-01 17:13:01 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( errno == ENOTBLK )
|
|
|
|
return true;
|
|
|
|
if ( errno == EINVAL )
|
|
|
|
return warning("%s: %m", path), true;
|
|
|
|
fatal("%s: %m", path);
|
2013-10-01 17:13:01 -04:00
|
|
|
}
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( hds_used == hds_length )
|
2013-10-01 17:13:01 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
// TODO: Potential overflow.
|
|
|
|
size_t new_length = hds_length ? 2 * hds_length : 16;
|
|
|
|
struct harddisk** new_hds = (struct harddisk**)
|
|
|
|
reallocarray(hds, new_length, sizeof(struct harddisk*));
|
|
|
|
if ( !new_hds )
|
|
|
|
fatal("realloc: %m");
|
|
|
|
hds = new_hds;
|
|
|
|
hds_length = new_length;
|
2013-10-01 17:13:01 -04:00
|
|
|
}
|
2015-07-23 20:24:49 -04:00
|
|
|
hds[hds_used++] = hd;
|
|
|
|
struct blockdevice* bdev = &hd->bdev;
|
|
|
|
enum partition_error parterr = blockdevice_get_partition_table(&bdev->pt, bdev);
|
|
|
|
if ( parterr == PARTITION_ERROR_ABSENT ||
|
|
|
|
parterr == PARTITION_ERROR_UNRECOGNIZED )
|
|
|
|
{
|
|
|
|
prepare_filesystem(path, bdev);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if ( parterr == PARTITION_ERROR_ERRNO )
|
|
|
|
{
|
|
|
|
if ( errno == EIO || errno == EINVAL )
|
|
|
|
warning("%s: %s", path, partition_error_string(parterr));
|
|
|
|
else
|
|
|
|
fatal("%s: %s", path, partition_error_string(parterr));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if ( parterr != PARTITION_ERROR_NONE )
|
|
|
|
{
|
|
|
|
warning("%s: %s", path, partition_error_string(parterr));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
for ( size_t i = 0; i < bdev->pt->partitions_count; i++ )
|
|
|
|
{
|
|
|
|
struct partition* p = bdev->pt->partitions[i];
|
|
|
|
assert(p->path);
|
|
|
|
struct stat st;
|
|
|
|
if ( stat(p->path, &st) == 0 )
|
|
|
|
{
|
|
|
|
// TODO: Check the existing partition has the right offset and
|
|
|
|
// length, but definitely do not recreate it if it already
|
|
|
|
// exists properly.
|
|
|
|
}
|
|
|
|
else if ( errno == ENOENT )
|
|
|
|
{
|
|
|
|
int mountfd = open(p->path, O_RDONLY | O_CREAT | O_EXCL);
|
|
|
|
if ( mountfd < 0 )
|
|
|
|
fatal("%s:˙%m", p->path);
|
|
|
|
int partfd = mkpartition(hd->fd, p->start, p->length);
|
|
|
|
if ( partfd < 0 )
|
|
|
|
fatal("mkpartition: %s:˙%m", p->path);
|
|
|
|
if ( fsm_fsbind(partfd, mountfd, 0) < 0 )
|
|
|
|
fatal("fsbind: %s:˙%m", p->path);
|
|
|
|
close(partfd);
|
|
|
|
close(mountfd);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fatal("stat: %s: %m", p->path);
|
|
|
|
}
|
|
|
|
prepare_filesystem(p->path, &p->bdev);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2013-10-01 17:13:01 -04:00
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
void prepare_block_devices()
|
|
|
|
{
|
|
|
|
static bool done = false;
|
|
|
|
if ( done )
|
|
|
|
return;
|
|
|
|
done = true;
|
2011-11-26 05:00:45 -05:00
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( !devices_iterate_path(prepare_block_device, NULL) )
|
|
|
|
fatal("iterating devices: %m");
|
2011-08-27 17:03:39 -04:00
|
|
|
}
|
2011-09-21 14:52:29 -04:00
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
struct device_match
|
2012-04-13 15:47:47 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
const char* path;
|
|
|
|
struct blockdevice* bdev;
|
|
|
|
};
|
2012-04-13 15:47:47 -04:00
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
void search_by_uuid(const char* uuid_string,
|
|
|
|
void (*cb)(void*, struct device_match*),
|
|
|
|
void* ctx)
|
|
|
|
{
|
|
|
|
unsigned char uuid[16];
|
|
|
|
uuid_from_string(uuid, uuid_string);
|
|
|
|
for ( size_t i = 0; i < hds_used; i++ )
|
2012-04-13 15:47:47 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
struct blockdevice* bdev = &hds[i]->bdev;
|
|
|
|
if ( bdev->fs )
|
|
|
|
{
|
|
|
|
struct filesystem* fs = bdev->fs;
|
|
|
|
if ( !(fs->flags & FILESYSTEM_FLAG_UUID) )
|
|
|
|
continue;
|
|
|
|
if ( memcmp(uuid, fs->uuid, 16) != 0 )
|
|
|
|
continue;
|
|
|
|
struct device_match match;
|
|
|
|
match.path = hds[i]->path;
|
|
|
|
match.bdev = bdev;
|
|
|
|
cb(ctx, &match);
|
|
|
|
}
|
|
|
|
else if ( bdev->pt )
|
2012-04-13 15:47:47 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
for ( size_t j = 0; j < bdev->pt->partitions_count; j++ )
|
|
|
|
{
|
|
|
|
struct partition* p = bdev->pt->partitions[j];
|
|
|
|
if ( !p->bdev.fs )
|
|
|
|
continue;
|
|
|
|
struct filesystem* fs = p->bdev.fs;
|
|
|
|
if ( !(fs->flags & FILESYSTEM_FLAG_UUID) )
|
|
|
|
continue;
|
|
|
|
if ( memcmp(uuid, fs->uuid, 16) != 0 )
|
|
|
|
continue;
|
|
|
|
struct device_match match;
|
|
|
|
match.path = p->path;
|
|
|
|
match.bdev = &p->bdev;
|
|
|
|
cb(ctx, &match);
|
|
|
|
}
|
2012-04-13 15:47:47 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
void ensure_single_device_match(void* ctx, struct device_match* match)
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
struct device_match* result = (struct device_match*) ctx;
|
|
|
|
if ( result->path )
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( result->bdev )
|
|
|
|
note("duplicate match: %s", result->path);
|
|
|
|
result->bdev = NULL;
|
|
|
|
note("duplicate match: %s", match->path);
|
|
|
|
return;
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
2015-07-23 20:24:49 -04:00
|
|
|
*result = *match;
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
void set_hostname()
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
FILE* fp = fopen("/etc/hostname", "r");
|
|
|
|
if ( !fp && errno == ENOENT )
|
|
|
|
return;
|
|
|
|
if ( !fp )
|
|
|
|
return warning("unable to set hostname: /etc/hostname: %m");
|
|
|
|
char* hostname = read_single_line(fp);
|
|
|
|
if ( !hostname )
|
|
|
|
return warning("unable to set hostname: /etc/hostname: %m");
|
|
|
|
fclose(fp);
|
|
|
|
int ret = sethostname(hostname, strlen(hostname) + 1);
|
|
|
|
free(hostname);
|
|
|
|
if ( ret < 0 )
|
|
|
|
return warning("unable to set hostname: `%s': %m", hostname);
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
void set_kblayout()
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
FILE* fp = fopen("/etc/kblayout", "r");
|
|
|
|
if ( !fp && errno == ENOENT )
|
2013-06-01 07:52:36 -04:00
|
|
|
return;
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( !fp )
|
|
|
|
return warning("unable to set keyboard layout: /etc/kblayout: %m");
|
|
|
|
char* kblayout = read_single_line(fp);
|
|
|
|
if ( !kblayout )
|
|
|
|
return warning("unable to set keyboard layout: /etc/kblayout: %m");
|
|
|
|
fclose(fp);
|
|
|
|
pid_t child_pid = fork();
|
|
|
|
if ( child_pid < 0 )
|
|
|
|
return warning("unable to set keyboard layout: fork: %m");
|
|
|
|
if ( !child_pid )
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
execlp("chkblayout", "chkblayout", "--", kblayout, (char*) NULL);
|
|
|
|
warning("setting keyboard layout: chkblayout: %m");
|
|
|
|
_exit(127);
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
2015-07-23 20:24:49 -04:00
|
|
|
int status;
|
|
|
|
waitpid(child_pid, &status, 0);
|
|
|
|
free(kblayout);
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
void set_videomode()
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
FILE* fp = fopen("/etc/videomode", "r");
|
|
|
|
if ( !fp && errno == ENOENT )
|
|
|
|
return;
|
|
|
|
if ( !fp )
|
|
|
|
return warning("unable to set video mode: /etc/videomode: %m");
|
|
|
|
char* videomode = read_single_line(fp);
|
|
|
|
if ( !videomode )
|
|
|
|
return warning("unable to set video mode: /etc/videomode: %m");
|
|
|
|
fclose(fp);
|
|
|
|
unsigned int xres = 0;
|
|
|
|
unsigned int yres = 0;
|
|
|
|
unsigned int bpp = 0;
|
|
|
|
if ( sscanf(videomode, "%ux%ux%u", &xres, &yres, &bpp) != 3 )
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
warning("/etc/videomode: Invalid video mode `%s'", videomode);
|
|
|
|
free(videomode);
|
|
|
|
return;
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
2015-07-23 20:24:49 -04:00
|
|
|
free(videomode);
|
|
|
|
struct dispmsg_set_crtc_mode set_mode;
|
|
|
|
memset(&set_mode, 0, sizeof(set_mode));
|
|
|
|
set_mode.msgid = DISPMSG_SET_CRTC_MODE;
|
|
|
|
set_mode.device = 0;
|
|
|
|
set_mode.connector = 0;
|
|
|
|
set_mode.mode.driver_index = 0;
|
|
|
|
set_mode.mode.magic = 0;
|
|
|
|
set_mode.mode.control = DISPMSG_CONTROL_VALID;
|
|
|
|
set_mode.mode.fb_format = bpp;
|
|
|
|
set_mode.mode.view_xres = xres;
|
|
|
|
set_mode.mode.view_yres = yres;
|
|
|
|
set_mode.mode.fb_location = 0;
|
|
|
|
set_mode.mode.pitch = xres * (bpp / 8);
|
|
|
|
set_mode.mode.surf_off_x = 0;
|
|
|
|
set_mode.mode.surf_off_y = 0;
|
|
|
|
set_mode.mode.start_x = 0;
|
|
|
|
set_mode.mode.start_y = 0;
|
|
|
|
set_mode.mode.end_x = 0;
|
|
|
|
set_mode.mode.end_y = 0;
|
|
|
|
set_mode.mode.desktop_height = yres;
|
|
|
|
if ( dispmsg_issue(&set_mode, sizeof(set_mode)) < 0 )
|
|
|
|
warning("/etc/videomode: Failed to set video mode `%ux%ux%u': %m",
|
|
|
|
xres, yres, bpp);
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
void init_early()
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
static bool done = false;
|
|
|
|
if ( done )
|
|
|
|
return;
|
|
|
|
done = true;
|
|
|
|
|
|
|
|
// Make sure that we have a /tmp directory.
|
|
|
|
umask(0000);
|
|
|
|
mkdir("/tmp", 01777);
|
|
|
|
|
|
|
|
// Set the default file creation mask.
|
|
|
|
umask(0022);
|
|
|
|
|
|
|
|
// Set up the PATH variable.
|
|
|
|
if ( setenv("PATH", "/bin:/sbin", 1) < 0 )
|
|
|
|
fatal("setenv: %m");
|
|
|
|
|
|
|
|
// Set the terminal type.
|
|
|
|
if ( setenv("TERM", "sortix", 1) < 0 )
|
|
|
|
fatal("setenv: %m");
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
int init(const char* target)
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( getenv("INIT_PID") )
|
|
|
|
fatal("System is already managed by an init process");
|
|
|
|
init_early();
|
|
|
|
set_hostname();
|
|
|
|
set_kblayout();
|
|
|
|
set_videomode();
|
|
|
|
prepare_block_devices();
|
|
|
|
sigset_t oldset, sigttou;
|
|
|
|
sigemptyset(&sigttou);
|
|
|
|
sigaddset(&sigttou, SIGTTOU);
|
|
|
|
while ( true )
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
struct termios tio;
|
|
|
|
if ( tcgetattr(0, &tio) )
|
|
|
|
fatal("tcgetattr: %m");
|
|
|
|
pid_t child_pid = fork();
|
|
|
|
if ( child_pid < 0 )
|
|
|
|
fatal("fork: %m");
|
|
|
|
if ( !child_pid )
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
uid_t uid = getuid();
|
|
|
|
pid_t pid = getpid();
|
|
|
|
pid_t ppid = getppid();
|
|
|
|
if ( setpgid(0, 0) < 0 )
|
|
|
|
fatal("setpgid: %m");
|
|
|
|
sigprocmask(SIG_BLOCK, &sigttou, &oldset);
|
|
|
|
if ( tcsetpgrp(0, pid) < 0 )
|
|
|
|
fatal("tcsetpgrp: %m");
|
|
|
|
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
|
|
|
struct passwd* pwd = getpwuid(uid);
|
|
|
|
if ( !pwd )
|
|
|
|
fatal("looking up user by uid %" PRIuUID ": %m", uid);
|
|
|
|
const char* home = pwd->pw_dir[0] ? pwd->pw_dir : "/";
|
|
|
|
const char* shell = pwd->pw_shell[0] ? pwd->pw_shell : "sh";
|
|
|
|
char ppid_str[sizeof(pid_t) * 3];
|
|
|
|
snprintf(ppid_str, sizeof(ppid_str), "%" PRIiPID, ppid);
|
|
|
|
if ( setenv("INIT_PID", ppid_str, 1) < 0 ||
|
|
|
|
setenv("LOGNAME", pwd->pw_name, 1) < 0 ||
|
|
|
|
setenv("USER", pwd->pw_name, 1) < 0 ||
|
|
|
|
setenv("HOME", home, 1) < 0 ||
|
|
|
|
setenv("SHELL", shell, 1) < 0 )
|
|
|
|
fatal("setenv: %m");
|
|
|
|
if ( chdir(home) < 0 )
|
|
|
|
warning("chdir: %s: %m", home);
|
|
|
|
const char* program = "login";
|
|
|
|
bool activate_terminal = false;
|
|
|
|
if ( !strcmp(target, "single-user") )
|
|
|
|
{
|
|
|
|
activate_terminal = true;
|
|
|
|
program = shell;
|
|
|
|
}
|
|
|
|
if ( activate_terminal )
|
|
|
|
{
|
|
|
|
tio.c_cflag |= CREAD;
|
|
|
|
if ( tcsetattr(0, TCSANOW, &tio) )
|
|
|
|
fatal("tcgetattr: %m");
|
|
|
|
}
|
|
|
|
const char* argv[] = { program, NULL };
|
|
|
|
execvp(program, (char* const*) argv);
|
|
|
|
fatal("%s: %m", program);
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
2015-07-23 20:24:49 -04:00
|
|
|
int status;
|
|
|
|
if ( waitpid(child_pid, &status, 0) < 0 )
|
|
|
|
fatal("waitpid");
|
|
|
|
sigprocmask(SIG_BLOCK, &sigttou, &oldset);
|
|
|
|
if ( tcsetattr(0, TCSAFLUSH, &tio) )
|
|
|
|
fatal("tcgetattr: %m");
|
|
|
|
if ( tcsetpgrp(0, getpgid(0)) < 0 )
|
|
|
|
fatal("tcsetpgrp: %m");
|
|
|
|
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
|
|
|
const char* back = ": Trying to bring it back up again";
|
|
|
|
if ( WIFEXITED(status) )
|
|
|
|
return WEXITSTATUS(status);
|
|
|
|
else if ( WIFSIGNALED(status) )
|
|
|
|
note("session: %s%s", strsignal(WTERMSIG(status)), back);
|
|
|
|
else
|
|
|
|
note("session: Unexpected unusual termination%s", back);
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
static pid_t chain_init_pid_by_path = -1;
|
|
|
|
static char* chain_mount_point_dev = NULL;
|
|
|
|
static void init_chain_by_path_unmount(void)
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( chain_init_pid_by_path != getpid() )
|
|
|
|
return;
|
|
|
|
if ( chain_mount_point_dev )
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
unmount(chain_mount_point_dev, 0);
|
|
|
|
free(chain_mount_point_dev);
|
|
|
|
chain_mount_point_dev = NULL;
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
2015-07-23 20:24:49 -04:00
|
|
|
}
|
2013-06-01 07:52:36 -04:00
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
int init_chain_by_path(const char* path)
|
|
|
|
{
|
|
|
|
struct stat rootst;
|
|
|
|
struct stat pathst;
|
|
|
|
if ( stat("/", &rootst) < 0 )
|
|
|
|
fatal("stat: /: %m");
|
|
|
|
if ( stat(path, &pathst) < 0 )
|
|
|
|
fatal("stat: %s: %m", path);
|
|
|
|
if ( rootst.st_dev == pathst.st_dev && rootst.st_ino == pathst.st_ino )
|
|
|
|
{
|
|
|
|
if ( getenv("INIT_PID") )
|
|
|
|
fatal("System is already managed by an init process");
|
|
|
|
}
|
|
|
|
init_early();
|
|
|
|
chain_init_pid_by_path = getpid();
|
|
|
|
if ( atexit(init_chain_by_path_unmount) != 0 )
|
|
|
|
fatal("atexit: %m");
|
|
|
|
if ( !(chain_mount_point_dev = join_paths(path, "dev")) )
|
|
|
|
fatal("asprintf: %m");
|
|
|
|
if ( mkdir(chain_mount_point_dev, 755) < 0 && errno != EEXIST )
|
|
|
|
fatal("mkdir: %s: %m", chain_mount_point_dev);
|
2013-06-01 07:52:36 -04:00
|
|
|
int old_dev_fd = open("/dev", O_DIRECTORY | O_RDONLY);
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( old_dev_fd < 0 )
|
|
|
|
fatal("%s: %m", "/dev");
|
|
|
|
int new_dev_fd = open(chain_mount_point_dev, O_DIRECTORY | O_RDONLY);
|
|
|
|
if ( new_dev_fd < 0 )
|
|
|
|
fatal("%s: %m", chain_mount_point_dev);
|
|
|
|
if ( fsm_fsbind(old_dev_fd, new_dev_fd, 0) < 0 )
|
|
|
|
fatal("mount: `%s' onto `%s': %m", "/dev", chain_mount_point_dev);
|
2013-06-01 07:52:36 -04:00
|
|
|
close(new_dev_fd);
|
|
|
|
close(old_dev_fd);
|
2015-07-23 20:24:49 -04:00
|
|
|
while ( true )
|
2014-09-30 16:59:34 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
pid_t child_pid = fork();
|
|
|
|
if ( child_pid < 0 )
|
|
|
|
fatal("fork: %m");
|
|
|
|
if ( !child_pid )
|
|
|
|
{
|
|
|
|
if ( chroot(path) < 0 )
|
|
|
|
fatal("chroot: %s: %m", path);
|
|
|
|
if ( chdir("/") < 0 )
|
|
|
|
fatal("chdir: %s: %m", path);
|
|
|
|
unsetenv("INIT_PID");
|
|
|
|
const char* argv[] = { "init", NULL };
|
|
|
|
execv("/sbin/init", (char* const*) argv);
|
|
|
|
fatal("Failed to load chain init: %s: %m", argv[0]);
|
|
|
|
}
|
|
|
|
int status;
|
|
|
|
if ( waitpid(child_pid, &status, 0) < 0 )
|
|
|
|
fatal("waitpid");
|
|
|
|
const char* back = ": Trying to bring it back up again";
|
|
|
|
if ( WIFEXITED(status) )
|
|
|
|
return WEXITSTATUS(status);
|
|
|
|
else if ( WIFSIGNALED(status) )
|
|
|
|
note("chain init: %s%s", strsignal(WTERMSIG(status)), back);
|
|
|
|
else
|
|
|
|
note("chain init: Unexpected unusual termination%s", back);
|
2014-09-30 16:59:34 -04:00
|
|
|
}
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
static pid_t chain_init_pid_by_filesystem = -1;
|
|
|
|
static char* chain_mount_point = NULL;
|
|
|
|
static pid_t chain_mount_pid = -1;
|
|
|
|
static void init_chain_by_filesystem_unmount(void)
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( chain_init_pid_by_filesystem != getpid() )
|
|
|
|
return;
|
|
|
|
if ( chain_mount_point )
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( unmount(chain_mount_point, 0) < 0 && errno != ENOMOUNT )
|
|
|
|
warning("unmount: %s: %m", chain_mount_point);
|
|
|
|
else if ( errno == ENOMOUNT && 0 <= chain_mount_pid )
|
|
|
|
kill(chain_mount_pid, SIGQUIT);
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( 0 <= chain_mount_pid )
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
int code;
|
|
|
|
if ( waitpid(chain_mount_pid, &code, 0) < 0 )
|
|
|
|
note("waitpid: %m");
|
|
|
|
chain_mount_pid = -1;
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( chain_mount_point )
|
2013-06-01 07:52:36 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( chain_mount_point && rmdir(chain_mount_point) < 0 )
|
|
|
|
warning("rmdir: %s: %m", chain_mount_point);
|
|
|
|
free(chain_mount_point);
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
2015-07-23 20:24:49 -04:00
|
|
|
chain_mount_point = NULL;
|
2013-06-01 07:52:36 -04:00
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
int init_chain_by_filesystem(struct filesystem* fs)
|
2011-09-21 14:52:29 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
// TODO: It would be ideal to get an exclusive lock so that no other
|
|
|
|
// processes have currently mounted that filesystem.
|
|
|
|
struct blockdevice* bdev = fs->bdev;
|
|
|
|
const char* bdev_path = bdev->p ? bdev->p->path : bdev->hd->path;
|
|
|
|
assert(bdev_path);
|
|
|
|
do if ( fs->flags & (FILESYSTEM_FLAG_FSCK_SHOULD | FILESYSTEM_FLAG_FSCK_MUST) )
|
2014-09-30 16:59:34 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
assert(fs->fsck);
|
|
|
|
if ( fs->flags & FILESYSTEM_FLAG_FSCK_MUST )
|
|
|
|
note("%s: Repairing filesystem due to inconsistency...", bdev_path);
|
|
|
|
else
|
|
|
|
note("%s: Checking filesystem consistency...", bdev_path);
|
|
|
|
pid_t child_pid = fork();
|
|
|
|
if ( child_pid < 0 )
|
|
|
|
{
|
|
|
|
if ( fs->flags & FILESYSTEM_FLAG_FSCK_MUST )
|
|
|
|
fatal("%s: Mandatory repair failed: fork: %m", bdev_path);
|
|
|
|
warning("%s: Skipping filesystem check: fork: %m:", bdev_path);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ( child_pid == 0 )
|
|
|
|
{
|
|
|
|
execlp(fs->fsck, fs->fsck, "-fp", "--", bdev_path, (const char*) NULL);
|
|
|
|
note("%s: Failed to load filesystem checker: %s: %m", bdev_path, fs->fsck);
|
|
|
|
_Exit(127);
|
|
|
|
}
|
|
|
|
int code;
|
|
|
|
if ( waitpid(child_pid, &code, 0) < 0 )
|
|
|
|
fatal("waitpid: %m");
|
|
|
|
if ( fs->flags & FILESYSTEM_FLAG_FSCK_MUST )
|
|
|
|
{
|
|
|
|
if ( WIFSIGNALED(code) )
|
|
|
|
fatal("%s: Mandatory repair failed: %s: %s", bdev_path,
|
|
|
|
fs->fsck, strsignal(WTERMSIG(code)));
|
|
|
|
else if ( !WIFEXITED(code) )
|
|
|
|
fatal("%s: Mandatory repair failed: %s: %s", bdev_path,
|
|
|
|
fs->fsck, "Unexpected unusual termination");
|
|
|
|
else if ( WEXITSTATUS(code) == 127 )
|
|
|
|
fatal("%s: Mandatory repair failed: %s: %s", bdev_path,
|
|
|
|
fs->fsck, "Filesystem checker is absent");
|
|
|
|
else if ( WEXITSTATUS(code) & 2 )
|
|
|
|
fatal("%s: Mandatory repair: %s: %s", bdev_path,
|
|
|
|
fs->fsck, "System reboot is necessary");
|
|
|
|
else if ( WEXITSTATUS(code) != 0 && WEXITSTATUS(code) != 1 )
|
|
|
|
fatal("%s: Mandatory repair failed: %s: %s", bdev_path,
|
|
|
|
fs->fsck, "Filesystem checker was unsuccessful");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( WIFSIGNALED(code) )
|
|
|
|
fatal("%s: Filesystem check failed: %s: %s", bdev_path,
|
|
|
|
fs->fsck, strsignal(WTERMSIG(code)));
|
|
|
|
else if ( !WIFEXITED(code) )
|
|
|
|
fatal("%s: Filesystem check failed: %s: %s", bdev_path,
|
|
|
|
fs->fsck, "Unexpected unusual termination");
|
|
|
|
else if ( WEXITSTATUS(code) == 127 )
|
|
|
|
warning("%s: Skipping filesystem check: %s: %s", bdev_path,
|
|
|
|
fs->fsck, "Filesystem checker is absent");
|
|
|
|
else if ( WEXITSTATUS(code) & 2 )
|
|
|
|
fatal("%s: Filesystem check: %s: %s", bdev_path,
|
|
|
|
fs->fsck, "System reboot is necessary");
|
|
|
|
else if ( WEXITSTATUS(code) != 0 && WEXITSTATUS(code) != 1 )
|
|
|
|
fatal("%s: Filesystem check failed: %s: %s", bdev_path,
|
|
|
|
fs->fsck, "Filesystem checker was unsuccessful");
|
|
|
|
}
|
|
|
|
} while ( 0 );
|
|
|
|
if ( !fs->driver )
|
|
|
|
fatal("%s: Don't know how to mount a %s filesystem",
|
|
|
|
bdev_path, fs->fstype_name);
|
|
|
|
chain_init_pid_by_filesystem = getpid();
|
|
|
|
if ( atexit(init_chain_by_filesystem_unmount) != 0 )
|
|
|
|
fatal("atexit: %m");
|
|
|
|
if ( !(chain_mount_point = strdup("/tmp/fs.XXXXXX")) )
|
|
|
|
fatal("strdup: %m");
|
|
|
|
if ( !mkdtemp(chain_mount_point) )
|
|
|
|
fatal("mkdtemp: %s: %m", "/tmp/fs.XXXXXX");
|
|
|
|
struct stat st;
|
|
|
|
if ( stat(chain_mount_point, &st) < 0 )
|
|
|
|
fatal("stat: %s: %m", chain_mount_point);
|
|
|
|
if ( (chain_mount_pid = fork()) < 0 )
|
|
|
|
fatal("%s: Unable to mount: fork: %m", bdev_path);
|
|
|
|
// TODO: This design is broken. The filesystem should tell us when it is
|
|
|
|
// ready instead of having to poll like this.
|
|
|
|
if ( chain_mount_pid == 0 )
|
2014-09-30 16:59:34 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
execlp(fs->driver, fs->driver, "--foreground", "--pretend-mount-path=/",
|
|
|
|
bdev_path, chain_mount_point, (const char*) NULL);
|
|
|
|
warning("%s: Failed to load filesystem driver: %s: %m", bdev_path, fs->driver);
|
|
|
|
_exit(127);
|
2014-09-30 16:59:34 -04:00
|
|
|
}
|
2015-07-23 20:24:49 -04:00
|
|
|
while ( true )
|
2014-09-30 16:59:34 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
struct stat newst;
|
|
|
|
if ( stat(chain_mount_point, &newst) < 0 )
|
|
|
|
fatal("stat: %s: %m", chain_mount_point);
|
|
|
|
if ( newst.st_dev != st.st_dev || newst.st_ino != st.st_ino )
|
|
|
|
break;
|
|
|
|
int code;
|
|
|
|
pid_t child = waitpid(chain_mount_pid, &code, WNOHANG);
|
|
|
|
if ( child < 0 )
|
|
|
|
fatal("waitpid: %m");
|
|
|
|
if ( child != 0 )
|
|
|
|
{
|
|
|
|
chain_mount_pid = -1;
|
|
|
|
if ( WIFSIGNALED(code) )
|
|
|
|
fatal("%s: Mount failed: %s: %s", bdev_path, fs->driver,
|
|
|
|
strsignal(WTERMSIG(code)));
|
|
|
|
else if ( !WIFEXITED(code) )
|
|
|
|
fatal("%s: Mount failed: %s: %s", bdev_path, fs->driver,
|
|
|
|
"Unexpected unusual termination");
|
|
|
|
else if ( WEXITSTATUS(code) == 127 )
|
|
|
|
fatal("%s: Mount failed: %s: %s", bdev_path, fs->driver,
|
|
|
|
"Filesystem driver is absent");
|
|
|
|
else if ( WEXITSTATUS(code) == 0 )
|
|
|
|
fatal("%s: Mount failed: %s: Unexpected successful exit",
|
|
|
|
bdev_path, fs->driver);
|
|
|
|
else
|
|
|
|
fatal("%s: Mount failed: %s: Exited with status %i", bdev_path,
|
|
|
|
fs->driver, WEXITSTATUS(code));
|
|
|
|
}
|
|
|
|
struct timespec delay = timespec_make(0, 50L * 1000L * 1000L);
|
|
|
|
nanosleep(&delay, NULL);
|
2014-09-30 16:59:34 -04:00
|
|
|
}
|
2015-07-23 20:24:49 -04:00
|
|
|
int result = init_chain_by_path(chain_mount_point);
|
|
|
|
init_chain_by_filesystem_unmount();
|
|
|
|
return result;
|
2014-09-30 16:59:34 -04:00
|
|
|
}
|
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
int init_chain_by_uuid(const char* uuid)
|
2015-03-21 12:28:52 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
init_early();
|
|
|
|
prepare_block_devices();
|
|
|
|
if ( !uuid_validate(uuid) )
|
|
|
|
fatal("`%s' is not a valid uuid", uuid);
|
|
|
|
struct device_match match;
|
|
|
|
memset(&match, 0, sizeof(match));
|
|
|
|
search_by_uuid(uuid, ensure_single_device_match, &match);
|
|
|
|
if ( !match.path )
|
|
|
|
fatal("No devices matching uuid %s were found", uuid);
|
|
|
|
if ( !match.bdev )
|
|
|
|
fatal("Don't know which particular device to boot with uuid %s", uuid);
|
|
|
|
assert(match.bdev->fs);
|
|
|
|
return init_chain_by_filesystem(match.bdev->fs);
|
|
|
|
}
|
2015-03-21 12:28:52 -04:00
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
int init_chain_by_uuid_file(const char* path)
|
|
|
|
{
|
|
|
|
// Find the uuid of the root filesystem.
|
|
|
|
FILE* fp = fopen(path, "r");
|
|
|
|
if ( !fp )
|
|
|
|
fatal("%s: %m", path);
|
|
|
|
char* uuid = read_single_line(fp);
|
|
|
|
if ( !uuid )
|
|
|
|
fatal("read: %s: %m", path);
|
|
|
|
fclose(fp);
|
|
|
|
if ( !uuid_validate(uuid) )
|
|
|
|
fatal("%s: `%s' is not a valid uuid", path, uuid);
|
|
|
|
int result = init_chain_by_uuid(uuid);
|
|
|
|
free(uuid);
|
|
|
|
return result;
|
|
|
|
}
|
2015-03-21 12:28:52 -04:00
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
int init_chain(const char* parameter)
|
|
|
|
{
|
|
|
|
bool parameter_was_null = parameter == NULL;
|
|
|
|
if ( !parameter )
|
2015-03-21 12:28:52 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( !setfsent() )
|
|
|
|
fatal("/etc/fstab: %m");
|
|
|
|
errno = 0;
|
|
|
|
struct fstab* fs = getfsfile("/");
|
|
|
|
if ( !fs && errno )
|
|
|
|
fatal("/etc/fstab: %m");
|
|
|
|
if ( !fs )
|
|
|
|
fatal("/etc/fstab: Root filesystem not found in filesystem table");
|
|
|
|
parameter = fs->fs_spec;
|
|
|
|
if ( strncmp(parameter, "UUID=", strlen("UUID=")) == 0 )
|
|
|
|
parameter += strlen("UUID=");
|
2015-03-21 12:28:52 -04:00
|
|
|
}
|
2015-07-23 20:24:49 -04:00
|
|
|
if ( uuid_validate(parameter) )
|
|
|
|
return init_chain_by_uuid(parameter);
|
|
|
|
struct stat st;
|
|
|
|
if ( stat(parameter, &st) < 0 )
|
|
|
|
error(2, errno, "%s", parameter);
|
|
|
|
if ( S_ISDIR(st.st_mode) && !parameter_was_null )
|
|
|
|
return init_chain_by_path(parameter);
|
|
|
|
// TODO: Implement this case so it works in /etc/fstab.
|
|
|
|
//else if ( S_ISBLK(st.st_mode) )
|
|
|
|
// return init_chain_by_device(parameter);
|
|
|
|
else if ( S_ISREG(st.st_mode) && !parameter_was_null )
|
|
|
|
return init_chain_by_uuid_file(parameter);
|
|
|
|
else
|
|
|
|
fatal("%s: Don't how how to chain initialize this", parameter);
|
|
|
|
}
|
2015-03-21 12:28:52 -04:00
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
static void compact_arguments(int* argc, char*** argv)
|
|
|
|
{
|
|
|
|
for ( int i = 0; i < *argc; i++ )
|
2015-03-21 12:28:52 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
while ( i < *argc && !(*argv)[i] )
|
|
|
|
{
|
|
|
|
for ( int n = i; n < *argc; n++ )
|
|
|
|
(*argv)[n] = (*argv)[n+1];
|
|
|
|
(*argc)--;
|
|
|
|
}
|
2015-03-21 12:28:52 -04:00
|
|
|
}
|
2015-07-23 20:24:49 -04:00
|
|
|
}
|
2015-03-21 12:28:52 -04:00
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
static void help(FILE* fp, const char* argv0)
|
|
|
|
{
|
|
|
|
fprintf(fp, "Usage: %s [OPTION]...\n", argv0);
|
|
|
|
fprintf(fp, "Initialize and manage the userland.\n");
|
|
|
|
}
|
2015-03-21 12:28:52 -04:00
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
static void version(FILE* fp, const char* argv0)
|
|
|
|
{
|
|
|
|
fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR);
|
|
|
|
fprintf(fp, "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n");
|
|
|
|
fprintf(fp, "This is free software: you are free to change and redistribute it.\n");
|
|
|
|
fprintf(fp, "There is NO WARRANTY, to the extent permitted by law.\n");
|
2015-03-21 12:28:52 -04:00
|
|
|
}
|
|
|
|
|
2015-07-31 15:59:53 -04:00
|
|
|
int main(int argc, char* argv[])
|
2014-09-30 16:59:34 -04:00
|
|
|
{
|
2015-07-23 20:24:49 -04:00
|
|
|
setlocale(LC_ALL, "");
|
2014-09-30 16:59:34 -04:00
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
const char* chain = NULL;
|
|
|
|
const char* target = NULL;
|
2013-05-16 16:03:15 -04:00
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
const char* argv0 = argv[0];
|
|
|
|
for ( int i = 1; i < argc; i++ )
|
|
|
|
{
|
|
|
|
const char* arg = argv[i];
|
|
|
|
if ( arg[0] != '-' || !arg[1] )
|
|
|
|
continue;
|
|
|
|
argv[i] = NULL;
|
|
|
|
if ( !strcmp(arg, "--") )
|
|
|
|
break;
|
|
|
|
if ( arg[1] != '-' )
|
|
|
|
{
|
|
|
|
while ( char c = *++arg ) switch ( c )
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
|
|
|
|
help(stderr, argv0);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ( !strncmp(arg, "--chain=", strlen("--chain=")) )
|
|
|
|
chain = arg + strlen("--chain=");
|
|
|
|
else if ( !strcmp(arg, "--chain") )
|
|
|
|
{
|
|
|
|
if ( i + 1 == argc )
|
|
|
|
{
|
|
|
|
error(0, 0, "option '--chain' requires an argument");
|
|
|
|
fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]);
|
|
|
|
exit(125);
|
|
|
|
}
|
|
|
|
chain = argv[i+1];
|
|
|
|
argv[++i] = NULL;
|
|
|
|
}
|
|
|
|
else if ( !strncmp(arg, "--target=", strlen("--target=")) )
|
|
|
|
target = arg + strlen("--target=");
|
|
|
|
else if ( !strcmp(arg, "--target") )
|
|
|
|
{
|
|
|
|
if ( i + 1 == argc )
|
|
|
|
{
|
|
|
|
error(0, 0, "option '--target' requires an argument");
|
|
|
|
fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]);
|
|
|
|
exit(125);
|
|
|
|
}
|
|
|
|
target = argv[i+1];
|
|
|
|
argv[++i] = NULL;
|
|
|
|
}
|
|
|
|
else if ( !strcmp(arg, "--help") )
|
|
|
|
help(stdout, argv0), exit(0);
|
|
|
|
else if ( !strcmp(arg, "--version") )
|
|
|
|
version(stdout, argv0), exit(0);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
|
|
|
|
help(stderr, argv0);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
2013-06-01 07:52:36 -04:00
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
compact_arguments(&argc, &argv);
|
2013-06-01 07:52:36 -04:00
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
char* target_string = NULL;
|
|
|
|
if ( !target )
|
|
|
|
{
|
|
|
|
const char* target_path = "/etc/init/target";
|
|
|
|
if ( chain )
|
|
|
|
target = "chain";
|
|
|
|
else if ( access(target_path, F_OK) == 0 )
|
|
|
|
{
|
|
|
|
FILE* target_fp = fopen(target_path, "r");
|
|
|
|
if ( !target_fp )
|
|
|
|
fatal("%s: %m", target_path);
|
|
|
|
target_string = read_single_line(target_fp);
|
|
|
|
if ( !target_string )
|
|
|
|
fatal("read: %s: %m", target_path);
|
|
|
|
target = target_string;
|
|
|
|
fclose(target_fp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
target = "single-user";
|
|
|
|
}
|
2013-06-01 07:52:36 -04:00
|
|
|
|
2015-07-23 20:24:49 -04:00
|
|
|
int result;
|
|
|
|
if ( !strcmp(target, "chain") )
|
|
|
|
return init_chain(chain);
|
|
|
|
else if ( !strcmp(target, "single-user") ||
|
|
|
|
!strcmp(target, "multi-user") )
|
|
|
|
return init(target);
|
|
|
|
else
|
|
|
|
fatal("Unknown initialization target `%s'", target);
|
|
|
|
free(target_string);
|
|
|
|
return result;
|
2011-09-21 14:52:29 -04:00
|
|
|
}
|