mirror of
				https://gitlab.com/sortix/sortix.git
				synced 2023-02-13 20:55:38 -05:00 
			
		
		
		
	Add sysinstall(8), sysmerge(8), and sysupgrade(8).
This commit is contained in:
		
							parent
							
								
									8af81a1864
								
							
						
					
					
						commit
						f52fb3202c
					
				
					 34 changed files with 4894 additions and 60 deletions
				
			
		
							
								
								
									
										35
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										35
									
								
								Makefile
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -24,6 +24,7 @@ mkinitrd \
 | 
			
		|||
regress \
 | 
			
		||||
sf \
 | 
			
		||||
sh \
 | 
			
		||||
sysinstall \
 | 
			
		||||
tix \
 | 
			
		||||
trianglix \
 | 
			
		||||
update-initrd \
 | 
			
		||||
| 
						 | 
				
			
			@ -67,37 +68,13 @@ SYSTEM_INITRD:=$(SORTIX_BUILDS_DIR)/$(BUILD_NAME).system.initrd
 | 
			
		|||
.PHONY: all
 | 
			
		||||
all: sysroot
 | 
			
		||||
 | 
			
		||||
.PHONY: install
 | 
			
		||||
install: sysroot
 | 
			
		||||
	@if test -z '$(INSTALL_ROOTFS)' ; then \
 | 
			
		||||
	  echo "error: You must set INSTALL_ROOTFS to where you want Sortix installed" >&2; \
 | 
			
		||||
	  exit 1; \
 | 
			
		||||
	fi
 | 
			
		||||
	@if test -d '$(INSTALL_ROOTFS)' && test -z '$(STUPIDLY_FORCE_SORTIX_INSTALL_OVERWRITE)'; then \
 | 
			
		||||
	  for ENTRY in $$(ls -A "$(SYSROOT)"); do \
 | 
			
		||||
	    if test -e "$(INSTALL_ROOTFS)/$$ENTRY"; then \
 | 
			
		||||
	      echo "Error: Refusing to corrupt the existing installation at $(INSTALL_ROOTFS)" >&2; \
 | 
			
		||||
	      echo "Use sysmerge to update an existence installation." >&2; \
 | 
			
		||||
	      exit 1; \
 | 
			
		||||
	    fi; \
 | 
			
		||||
	  done; \
 | 
			
		||||
	fi
 | 
			
		||||
	cp -RTv "$(SYSROOT)" "$(INSTALL_ROOTFS)"
 | 
			
		||||
 | 
			
		||||
.PHONY: sysmerge
 | 
			
		||||
sysmerge: sysroot
 | 
			
		||||
ifeq ($(BUILD_IS_SORTIX),0)
 | 
			
		||||
	if test -z '$(DESTDIR)' || test 'x$(DESTDIR)' = 'x/'; then \
 | 
			
		||||
	  echo "error: Refusing to corrupt the local operating system by sysmerging it with Sortix" >&2 \
 | 
			
		||||
	  exit 1 \
 | 
			
		||||
	fi
 | 
			
		||||
endif
 | 
			
		||||
	for ENTRY in $$(ls -A "$(SYSROOT)" | grep -Ev '^(dev|etc|home|mnt|root|src|tix|tmp|var)$$'); do \
 | 
			
		||||
		cp -RTv "$(SYSROOT)/$$ENTRY" "$(DESTDIR)/$$ENTRY" || exit $$?; \
 | 
			
		||||
	done
 | 
			
		||||
	cp -TPv "$(SYSROOT)/etc/machine" "$(DESTDIR)/etc/machine"
 | 
			
		||||
	cp -TPv "$(SYSROOT)/etc/os-release" "$(DESTDIR)/etc/os-release"
 | 
			
		||||
	cp -TPv "$(SYSROOT)/etc/sortix-release" "$(DESTDIR)/etc/sortix-release"
 | 
			
		||||
	"$(SYSROOT)/sbin/sysmerge" "$(SYSROOT)"
 | 
			
		||||
 | 
			
		||||
.PHONY: sysmerge-wait
 | 
			
		||||
sysmerge-wait: sysroot
 | 
			
		||||
	"$(SYSROOT)/sbin/sysmerge" --wait "$(SYSROOT)"
 | 
			
		||||
 | 
			
		||||
.PHONY: clean-build-tools
 | 
			
		||||
clean-build-tools:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -138,3 +138,5 @@ EOF
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
menuentry "live environment" ''
 | 
			
		||||
menuentry "new installation" '--init="/sbin/init --target=sysinstall"'
 | 
			
		||||
menuentry "upgrade existing installation" '--init="/sbin/init --target=sysupgrade"'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,7 @@
 | 
			
		|||
You can view the documentation for new users by typing:
 | 
			
		||||
 | 
			
		||||
  man user-guide
 | 
			
		||||
 | 
			
		||||
You can view the installation instructions by typing:
 | 
			
		||||
 | 
			
		||||
  man installation
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,6 +43,10 @@ otherwise. Supported targets are:
 | 
			
		|||
.It chain
 | 
			
		||||
mount real root filesystem and run its
 | 
			
		||||
.Nm
 | 
			
		||||
.It chain-merge
 | 
			
		||||
complete a
 | 
			
		||||
.Xr sysmerge 8
 | 
			
		||||
upgrade during a chain boot
 | 
			
		||||
.It multi-user
 | 
			
		||||
boot to
 | 
			
		||||
.Xr login 8
 | 
			
		||||
| 
						 | 
				
			
			@ -173,4 +177,5 @@ exits with the same exit status as its target session if it terminates normally.
 | 
			
		|||
.Xr initrd 7 ,
 | 
			
		||||
.Xr kernel 7 ,
 | 
			
		||||
.Xr login 8 ,
 | 
			
		||||
.Xr sysmerge 8 ,
 | 
			
		||||
.Xr update-initrd 8
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -765,6 +765,16 @@ static int init(const char* target)
 | 
			
		|||
				activate_terminal = true;
 | 
			
		||||
				program = shell;
 | 
			
		||||
			}
 | 
			
		||||
			if ( !strcmp(target, "sysinstall") )
 | 
			
		||||
			{
 | 
			
		||||
				activate_terminal = true;
 | 
			
		||||
				program = "sysinstall";
 | 
			
		||||
			}
 | 
			
		||||
			if ( !strcmp(target, "sysupgrade") )
 | 
			
		||||
			{
 | 
			
		||||
				program = "sysupgrade";
 | 
			
		||||
				activate_terminal = true;
 | 
			
		||||
			}
 | 
			
		||||
			if ( activate_terminal )
 | 
			
		||||
			{
 | 
			
		||||
				tio.c_cflag |= CREAD;
 | 
			
		||||
| 
						 | 
				
			
			@ -868,16 +878,40 @@ static int init_chain(const char* target)
 | 
			
		|||
				fatal("chroot: %s: %m", chain_location);
 | 
			
		||||
			if ( chdir("/") < 0 )
 | 
			
		||||
				fatal("chdir: %s: %m", chain_location);
 | 
			
		||||
			(void) target;
 | 
			
		||||
			unsetenv("INIT_PID");
 | 
			
		||||
			const char* argv[] = { "init", NULL };
 | 
			
		||||
			execv("/sbin/init", (char* const*) argv);
 | 
			
		||||
			fatal("Failed to load chain init: %s: %m", argv[0]);
 | 
			
		||||
			if ( !strcmp(target, "chain-merge") )
 | 
			
		||||
			{
 | 
			
		||||
				const char* argv[] = { "sysmerge", "--booting", NULL };
 | 
			
		||||
				execv("/sysmerge/sbin/sysmerge", (char* const*) argv);
 | 
			
		||||
				fatal("Failed to run automatic update: %s: %m", argv[0]);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				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 ( !strcmp(target, "chain-merge") )
 | 
			
		||||
		{
 | 
			
		||||
			if ( WIFEXITED(status) && WEXITSTATUS(status) == 0 )
 | 
			
		||||
			{
 | 
			
		||||
				target = "chain";
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if ( WIFEXITED(status) )
 | 
			
		||||
				fatal("Automatic upgrade failed: Exit status %i",
 | 
			
		||||
				      WEXITSTATUS(status));
 | 
			
		||||
			else if ( WIFSIGNALED(status) )
 | 
			
		||||
				fatal("Automatic upgrade failed: %s",
 | 
			
		||||
				      strsignal(WTERMSIG(status)));
 | 
			
		||||
			else
 | 
			
		||||
				fatal("Automatic upgrade failed: Unexpected unusual termination");
 | 
			
		||||
		}
 | 
			
		||||
		if ( WIFEXITED(status) )
 | 
			
		||||
		{
 | 
			
		||||
			result = WEXITSTATUS(status);
 | 
			
		||||
| 
						 | 
				
			
			@ -990,17 +1024,20 @@ int main(int argc, char* argv[])
 | 
			
		|||
			fclose(target_fp);
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			target = "single-user";
 | 
			
		||||
			fatal("Refusing to initialize because %s doesn't exist", target_path);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( getenv("INIT_PID") )
 | 
			
		||||
		fatal("System is already managed by an init process");
 | 
			
		||||
 | 
			
		||||
	if ( !strcmp(target, "single-user") ||
 | 
			
		||||
	     !strcmp(target, "multi-user") )
 | 
			
		||||
	     !strcmp(target, "multi-user") ||
 | 
			
		||||
	     !strcmp(target, "sysinstall") ||
 | 
			
		||||
	     !strcmp(target, "sysupgrade") )
 | 
			
		||||
		return init(target);
 | 
			
		||||
 | 
			
		||||
	if ( !strcmp(target, "chain") )
 | 
			
		||||
	if ( !strcmp(target, "chain") ||
 | 
			
		||||
	     !strcmp(target, "chain-merge") )
 | 
			
		||||
		return init_chain(target);
 | 
			
		||||
 | 
			
		||||
	fatal("Unknown initialization target `%s'", target);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										105
									
								
								share/man/man5/upgrade.conf.5
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								share/man/man5/upgrade.conf.5
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,105 @@
 | 
			
		|||
.Dd $Mdocdate: January 4 2016 $
 | 
			
		||||
.Dt UPGRADE.CONF 5
 | 
			
		||||
.Os
 | 
			
		||||
.Sh NAME
 | 
			
		||||
.Nm upgrade.conf
 | 
			
		||||
.Nd upgrade configuration
 | 
			
		||||
.Sh SYNOPSIS
 | 
			
		||||
.Nm /etc/upgrade.conf
 | 
			
		||||
.Sh DESCRIPTION
 | 
			
		||||
The
 | 
			
		||||
.Nm upgrade.conf
 | 
			
		||||
controls the actions taken by
 | 
			
		||||
.Xr sysupgrade 8
 | 
			
		||||
during a system upgrade as described in
 | 
			
		||||
.Xr upgrade 7 .
 | 
			
		||||
The file allows customizing whether the system itself is replaced, whether new
 | 
			
		||||
ports are installed, whether the new source code is installed and what happens
 | 
			
		||||
to the old source code.  It also records information about the system such as
 | 
			
		||||
how the system is booted. The file is created automatically by
 | 
			
		||||
.Xr sysinstall 8
 | 
			
		||||
as part of
 | 
			
		||||
.Xr installation 7
 | 
			
		||||
to match what was installed.
 | 
			
		||||
.Pp
 | 
			
		||||
Developers may wish to customize what happens to
 | 
			
		||||
.Pa /src
 | 
			
		||||
on a system upgrade. The new source code can be installed or not. If it is
 | 
			
		||||
installed, it can be installed in
 | 
			
		||||
.Pa /newsrc
 | 
			
		||||
or by default in
 | 
			
		||||
.Pa /src .
 | 
			
		||||
Any existing source code will be safely moved inside a subdirectory of
 | 
			
		||||
.Pa /oldsrc .
 | 
			
		||||
.Pp
 | 
			
		||||
It is possible to disable any upgrading whatsoever.  If you do this, except the
 | 
			
		||||
new source code is installed, then you can upgrade to the new system manually as
 | 
			
		||||
described in
 | 
			
		||||
.Xr development 7 .
 | 
			
		||||
Upgrading to the next release from source is not supported.
 | 
			
		||||
.Sh FORMAT
 | 
			
		||||
The file is read line by line. The # character starts a comment and the rest of
 | 
			
		||||
the line is ignored. Lines are supposed to contain assignments to variables.  An
 | 
			
		||||
assignment is the name of the variable, whitespace, an equal character,
 | 
			
		||||
whitespace, the value, whitespace, and then the end of the line.
 | 
			
		||||
.Bl -tag -width "12345678"
 | 
			
		||||
.It Sy grub Ns "=" Ns Oo Sy no "|" yes Oc (default Sy no ) .
 | 
			
		||||
States GRUB is used as the bootloader.  If either the
 | 
			
		||||
.Sy system
 | 
			
		||||
or
 | 
			
		||||
.Sy ports
 | 
			
		||||
are set to
 | 
			
		||||
.Sy yes ,
 | 
			
		||||
then the bootloader is reinstalled
 | 
			
		||||
.Xr ( grub-install 8 )
 | 
			
		||||
and updated
 | 
			
		||||
.Xr ( update-grub 8 ) .
 | 
			
		||||
.It Sy newsrc Ns "=" Ns Oo Sy no "|" yes Oc (default Sy no ) .
 | 
			
		||||
Place the new source code in
 | 
			
		||||
.Pa /newsrc
 | 
			
		||||
and move any existing
 | 
			
		||||
.Pa /newsrc
 | 
			
		||||
into
 | 
			
		||||
.Pa /oldsrc .
 | 
			
		||||
This preserves the current
 | 
			
		||||
.Pa /src
 | 
			
		||||
directory.  This takes precedence over and disables the behavior described under
 | 
			
		||||
.Sy src .
 | 
			
		||||
.It Sy ports Ns "=" Ns Oo Sy no "|" yes Oc (default Sy yes ) .
 | 
			
		||||
Install the new ports.
 | 
			
		||||
.It Sy src Ns "=" Ns Oo Sy no "|" yes Oc (default Sy no ) .
 | 
			
		||||
Place the new source code in
 | 
			
		||||
.Pa /src
 | 
			
		||||
and move any existing
 | 
			
		||||
.Pa /src
 | 
			
		||||
into
 | 
			
		||||
.Pa /oldsrc .
 | 
			
		||||
.It Sy system Ns "=" Ns Oo Sy no "|" yes Oc (default Sy yes ) .
 | 
			
		||||
Install the new system.  This will run
 | 
			
		||||
.Xr update-initrd 8
 | 
			
		||||
and if
 | 
			
		||||
.Sy grub
 | 
			
		||||
is set to
 | 
			
		||||
.Sy no ,
 | 
			
		||||
then regenerate
 | 
			
		||||
.Pa /etc/grub.d/10_sortix.cache .
 | 
			
		||||
.El
 | 
			
		||||
.Pp
 | 
			
		||||
The defaults will be used if
 | 
			
		||||
.Pa /etc/upgrade.conf
 | 
			
		||||
is missing.
 | 
			
		||||
.Sh FILES
 | 
			
		||||
.Bl -tag -width "/etc/upgrade.conf" -compact
 | 
			
		||||
.It Pa /etc/upgrade.conf
 | 
			
		||||
Upgrade configuration.
 | 
			
		||||
.El
 | 
			
		||||
.Sh EXAMPLES
 | 
			
		||||
.Bd -literal
 | 
			
		||||
system = yes
 | 
			
		||||
ports = yes
 | 
			
		||||
src = no
 | 
			
		||||
grub = yes
 | 
			
		||||
.Ed
 | 
			
		||||
.Sh SEE ALSO
 | 
			
		||||
.Xr upgrade 7 ,
 | 
			
		||||
.Xr sysupgrade 8
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +39,6 @@ system, run as root:
 | 
			
		|||
    cd /src
 | 
			
		||||
    make            # build new operating system in /src/sysroot
 | 
			
		||||
    make sysmerge   # upgrade current operating system with /src/sysroot
 | 
			
		||||
    update-initrd   # update boot initrd with new files
 | 
			
		||||
.Ed
 | 
			
		||||
.Pp
 | 
			
		||||
The build system creates a minimal root filesystem structure in the
 | 
			
		||||
| 
						 | 
				
			
			@ -55,20 +54,22 @@ The
 | 
			
		|||
.Sy sysmerge
 | 
			
		||||
make target ensures a system is built in
 | 
			
		||||
.Pa /src/sysroot
 | 
			
		||||
and then uses it to replace the current operating system.  It ignores
 | 
			
		||||
configuration files and other things that could cause conflicts when merging
 | 
			
		||||
with an existing system.  The new user-space is running on completition, though
 | 
			
		||||
existing processes will be running the old programs. Likewise a reboot is needed
 | 
			
		||||
to run the new kernel.
 | 
			
		||||
.Pp
 | 
			
		||||
The
 | 
			
		||||
.Xr update-initrd 8
 | 
			
		||||
program regenerates the
 | 
			
		||||
and then runs the
 | 
			
		||||
.Xr sysmerge 8
 | 
			
		||||
program which installs the new system files onto the existing system.  It
 | 
			
		||||
updates the system manifest as well all ports installed in the sysroot.  The
 | 
			
		||||
.Xr initrd 7
 | 
			
		||||
by embedding configuration and programs from the root filesystem.  It needs to
 | 
			
		||||
be run when init system files,
 | 
			
		||||
.Xr fstab 5 ,
 | 
			
		||||
filesystem drivers, or filesystem checkers are modified.
 | 
			
		||||
is automatically regenerated using
 | 
			
		||||
.Xr update-initrd 8 .
 | 
			
		||||
The bootloader, if enabled in
 | 
			
		||||
.Xr upgrade.conf 5 ,
 | 
			
		||||
is reinstalled and configured as necessary.  The new user-space is running on
 | 
			
		||||
completion, though existing processes will be running the old programs.
 | 
			
		||||
A reboot is needed to run the new kernel.  If the ABI changed and the current
 | 
			
		||||
kernel isn't able to run the new programs, then the upgrade is delayed and will
 | 
			
		||||
be automatically completed on the next boot.  The
 | 
			
		||||
.Sy sysmerge-wait
 | 
			
		||||
make target forces waiting until the next boot.
 | 
			
		||||
.Ss Root Makefile
 | 
			
		||||
The
 | 
			
		||||
.Pa /src/Makefile
 | 
			
		||||
| 
						 | 
				
			
			@ -112,6 +113,10 @@ and place it in the current directory as
 | 
			
		|||
Upgrade the current operating system using the sysroot after making the
 | 
			
		||||
.Sy all
 | 
			
		||||
target.
 | 
			
		||||
.It Sy sysmerge-wait
 | 
			
		||||
Like
 | 
			
		||||
.Sy sysmerge
 | 
			
		||||
but delay the upgrade until the next boot.
 | 
			
		||||
.It Sy sysroot-base-headers
 | 
			
		||||
Create the sysroot and install only the headers of the standard library and
 | 
			
		||||
kernel into it.  This is useful when bootstrapping the runtime libraries of the
 | 
			
		||||
| 
						 | 
				
			
			@ -314,6 +319,10 @@ formal online release.
 | 
			
		|||
.Xr git 1 ,
 | 
			
		||||
.Xr make 1 ,
 | 
			
		||||
.Xr cross-development 7 ,
 | 
			
		||||
.Xr installation 7 ,
 | 
			
		||||
.Xr porting-guide 7 ,
 | 
			
		||||
.Xr serial-transfer 7 ,
 | 
			
		||||
.Xr upgrade 7 ,
 | 
			
		||||
.Xr sysinstall 8 ,
 | 
			
		||||
.Xr sysmerge 8 ,
 | 
			
		||||
.Xr update-initrd 8
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,7 @@
 | 
			
		|||
.Nd layout of filesystems
 | 
			
		||||
.Sh DESCRIPTION
 | 
			
		||||
The filesystem hierarchy is as follows:
 | 
			
		||||
.Bl -tag -width "12345678"
 | 
			
		||||
.Bl -tag -width "12345678910"
 | 
			
		||||
.It Pa /
 | 
			
		||||
Root directory.
 | 
			
		||||
.It Pa /bin
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +38,10 @@ System programs.
 | 
			
		|||
Architecture independent files.
 | 
			
		||||
.It Pa /src
 | 
			
		||||
System source code.
 | 
			
		||||
.It Pa /sysmerge
 | 
			
		||||
Temporary area for
 | 
			
		||||
.Xr sysmerge 8
 | 
			
		||||
delayed upgrades.
 | 
			
		||||
.It Pa /tix
 | 
			
		||||
Package management
 | 
			
		||||
.It Pa /tmp
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										338
									
								
								share/man/man7/installation.7
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										338
									
								
								share/man/man7/installation.7
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,338 @@
 | 
			
		|||
.Dd $Mdocdate: December 25 2015 $
 | 
			
		||||
.Dt INSTALLATION 7
 | 
			
		||||
.Os
 | 
			
		||||
.Sh NAME
 | 
			
		||||
.Nm installation
 | 
			
		||||
.Nd operating system installation instructions
 | 
			
		||||
.Sh DESCRIPTION
 | 
			
		||||
This document describes how to install Sortix on a computer from a cdrom
 | 
			
		||||
release.  Please read it through carefully before beginning the installation so
 | 
			
		||||
you know what to expect and things you need to keep in mind.  The
 | 
			
		||||
.Xr upgrade 7
 | 
			
		||||
manual page covers upgrading an existing installation.
 | 
			
		||||
.Ss Prerequisites
 | 
			
		||||
.Bl -bullet -compact
 | 
			
		||||
.It
 | 
			
		||||
A
 | 
			
		||||
.Pa sortix-x.y-arch.iso
 | 
			
		||||
release for your architecture.
 | 
			
		||||
.It
 | 
			
		||||
A cdrom onto which the release has been burned, or USB portable storage onto
 | 
			
		||||
which the release has been placed at the first byte and onwards.
 | 
			
		||||
.It
 | 
			
		||||
A computer meeting the system requirements.
 | 
			
		||||
.El
 | 
			
		||||
.Ss System Requirements
 | 
			
		||||
.Bl -bullet -compact
 | 
			
		||||
.It
 | 
			
		||||
32-bit x86 CPU with SSE (i486 release), or 64-bit x86 CPU (x86_64 release).
 | 
			
		||||
.It
 | 
			
		||||
1 GiB RAM (recommended) to run iso live environment (including installer) with
 | 
			
		||||
all ports loaded, or significantly less if unimportant ports are not loaded.  An
 | 
			
		||||
installation on a harddisk will require very little RAM to run after
 | 
			
		||||
installation.
 | 
			
		||||
.It
 | 
			
		||||
ATA or AHCI harddisk with at least 1 GiB of unpartitioned space.
 | 
			
		||||
.It
 | 
			
		||||
BIOS firmware, or UEFI firmware in legacy mode.
 | 
			
		||||
.It
 | 
			
		||||
PS/2 keyboard/mouse firmware emulation to use those devices.
 | 
			
		||||
.It
 | 
			
		||||
If you wish to dual boot, you need an existing operating system with a multiboot
 | 
			
		||||
compliant bootloader such as GRUB.
 | 
			
		||||
.El
 | 
			
		||||
.Ss Preparation
 | 
			
		||||
Read this document through before beginning the installation.  The installation
 | 
			
		||||
process is designed to be reasonable, but you need to patient and in an
 | 
			
		||||
emotionally stable place.  It is important you understand the current
 | 
			
		||||
limitations of the system and carefully consider whether you want to go through
 | 
			
		||||
with the installation at this time.
 | 
			
		||||
.Pp
 | 
			
		||||
Before installing any operating system, be sure to have backed up local data in
 | 
			
		||||
the event something goes wrong.  This operating system comes without any
 | 
			
		||||
warranty at all (see the license).
 | 
			
		||||
.Pp
 | 
			
		||||
Consider the partitioning scheme and whether you wish to dual boot.  Consult the
 | 
			
		||||
partitioning instructions below. If dual-booting and there isn't enough
 | 
			
		||||
unpartitioned space, use the native partition editor of the existing operating
 | 
			
		||||
system to shrink its installation.
 | 
			
		||||
.Pp
 | 
			
		||||
Determine how the target machine will boot the release.  If the firmware
 | 
			
		||||
supports usb-iso hybrid images, you can use
 | 
			
		||||
.Xr dd 1
 | 
			
		||||
or such to copy the release physically onto a USB portable storage device.
 | 
			
		||||
If the target machine has a cdrom drive, you can burn the release to a cdrom.
 | 
			
		||||
Insert the installation medium in the computer and power it on.  If needed,
 | 
			
		||||
change the boot order in the firmware to prefer the installation medium over any
 | 
			
		||||
existing operating system installations.
 | 
			
		||||
.Pp
 | 
			
		||||
After the installation is complete, remove the installation medium and restore
 | 
			
		||||
the firmware boot order to prioritize the local harddisk.  Then power the
 | 
			
		||||
computer on normally to run the new operating system.
 | 
			
		||||
.Ss Qemu
 | 
			
		||||
Virtual machines are a well-supported installation target.  For instance, to
 | 
			
		||||
prepare a 1 GiB harddisk and install the operating system onto it, run something
 | 
			
		||||
like:
 | 
			
		||||
.Bd -literal
 | 
			
		||||
qemu-img create sortix.raw 1G
 | 
			
		||||
qemu-system-x86_64 -enable-kvm -m 1024 -vga std -cdrom sortix.iso \\
 | 
			
		||||
                   -drive file=sortix.raw,format=raw
 | 
			
		||||
.Ed
 | 
			
		||||
.Pp
 | 
			
		||||
After the installation is complete, power off the computer and remove the
 | 
			
		||||
.Ar -cdrom
 | 
			
		||||
.Pa sortix.iso
 | 
			
		||||
option.
 | 
			
		||||
.Ss Bootloader Menu
 | 
			
		||||
Booting the release will present you with a GRUB bootloader menu.  You have
 | 
			
		||||
three primary options:
 | 
			
		||||
.Pp
 | 
			
		||||
.Bl -bullet -compact
 | 
			
		||||
.It
 | 
			
		||||
Running a fully-featured temporary live environment.
 | 
			
		||||
.It
 | 
			
		||||
Running the operating system installer
 | 
			
		||||
.Xr ( sysinstall 8 ) .
 | 
			
		||||
.It
 | 
			
		||||
Upgrading an existing installation to this release
 | 
			
		||||
.Xr ( sysupgrade 8 ) .
 | 
			
		||||
.El
 | 
			
		||||
.Pp
 | 
			
		||||
Each of these options are a live environment running exclusively in RAM.  The
 | 
			
		||||
difference is only what program is run after the system has booted.  The
 | 
			
		||||
bootloader will load the whole operating system and ports into memory from the
 | 
			
		||||
installation medium.  This may take a moment.  You need enough memory to store
 | 
			
		||||
the whole system and the runtime usage.  If the system memory is really
 | 
			
		||||
insufficient, then the bootloader may have strange behavior, take a really long
 | 
			
		||||
time to load, or not complete the boot at all.
 | 
			
		||||
.Pp
 | 
			
		||||
You can configure which ports gets loaded using the bootloader menu.  The base
 | 
			
		||||
system is rather lean and can be made quite small.  You need some ports to
 | 
			
		||||
complete an installation.
 | 
			
		||||
.Ss Installer
 | 
			
		||||
This guide assumes you selected the operating system installation option in the
 | 
			
		||||
bootloader.  If not, you can run the installer by running the
 | 
			
		||||
.Xr sysinstall 8
 | 
			
		||||
command.
 | 
			
		||||
.Pp
 | 
			
		||||
The installer is an interactive command line program that asks you questions and
 | 
			
		||||
you answer them.  It provides useful information you shouldn't accidentally
 | 
			
		||||
overlook.  Before answering any question, read all output since your last
 | 
			
		||||
answer.
 | 
			
		||||
.Pp
 | 
			
		||||
You should have this installation guide ready at all times.  You can view this
 | 
			
		||||
.Xr installation 7
 | 
			
		||||
page during the installation by answering
 | 
			
		||||
.Sy '!man'
 | 
			
		||||
to any regular prompt (excluding password prompts).  Likewise you can answer
 | 
			
		||||
.Sy '!'
 | 
			
		||||
to get an interactive shell.  Upon completion, you will be asked the question
 | 
			
		||||
again.
 | 
			
		||||
.Ss Keyboard Layout
 | 
			
		||||
You need to choose the applicable keyboard layout.  By default, a standard US
 | 
			
		||||
keyboard layout is used.  You can view a list of keyboard layouts if you wish.
 | 
			
		||||
This layout is then loaded and the preference will be stored in
 | 
			
		||||
.Xr kblayout 5 .
 | 
			
		||||
.Ss Display Resolution
 | 
			
		||||
If a driver exists for your graphics card, then you will be asked for your
 | 
			
		||||
preferred display resolution by
 | 
			
		||||
.Xr chvideomode 1 .
 | 
			
		||||
The display will then use this resolution and your preference will be stored in
 | 
			
		||||
.Xr videomode 5 .
 | 
			
		||||
.Ss Bootloader
 | 
			
		||||
The
 | 
			
		||||
.Xr kernel 7
 | 
			
		||||
is a multiboot compatible binary that can be loaded by any multiboot
 | 
			
		||||
specification compatible bootloader such as GRUB.  You need to use such a
 | 
			
		||||
bootloader to boot the operating system.  You will be offered the choice of
 | 
			
		||||
installing GRUB as the bootloader.  Note however that this GRUB is not able to
 | 
			
		||||
detect other operating systems and you will have to configure it manually if you
 | 
			
		||||
wish to use it in a dual boot scheme.  The answer will default to yes if no
 | 
			
		||||
existing partitions are found, and will default to no if some are found.
 | 
			
		||||
.Pp
 | 
			
		||||
Single-boot configurations should use the offered bootloader.  Dual-boot
 | 
			
		||||
configurations should refuse it and arrange for bootloading by other means.  The
 | 
			
		||||
installer will generate
 | 
			
		||||
.Pa /etc/grub.d/10_sortix.cache
 | 
			
		||||
which is a fragment of GRUB configuration that offers the menu option of running
 | 
			
		||||
Sortix.  You can splice that into
 | 
			
		||||
.Pa /etc/grub.d/40_custom
 | 
			
		||||
of an existing GRUB installation and run
 | 
			
		||||
.Xr update-grub 8
 | 
			
		||||
to add it as a boot option.
 | 
			
		||||
.Pp
 | 
			
		||||
If you accept the bootloader, you will be asked if you wish to password protect
 | 
			
		||||
the bootloader.  If you don't, anyone able to use the keyboard during system
 | 
			
		||||
bootloading will be trivially able to gain root access using the bootloader
 | 
			
		||||
command line.  If you use this, you should also password protect the firmware and
 | 
			
		||||
prohibit it from booting from anything but the harddisk.  An attacker will then
 | 
			
		||||
need to tamper with the computer itself physically.  The password will be hashed
 | 
			
		||||
and stored in
 | 
			
		||||
.Xr grubpw 5
 | 
			
		||||
and is inserted into the GRUB configuration when
 | 
			
		||||
.Xr update-grub 8
 | 
			
		||||
is run.
 | 
			
		||||
.Ss Partitioning
 | 
			
		||||
You will now need to set up a partition for the root filesystem and other
 | 
			
		||||
filesystems you wish to use.  The installer will give you instructions and run
 | 
			
		||||
the
 | 
			
		||||
.Xr disked 8
 | 
			
		||||
partitioning program.  You can view its man page by typing
 | 
			
		||||
.Sy man
 | 
			
		||||
and you can view this man page by typing
 | 
			
		||||
.Sy man 7 installation .
 | 
			
		||||
.Pp
 | 
			
		||||
.Nm disked
 | 
			
		||||
defaults to the first detected harddisk as the current harddisk.  You can switch
 | 
			
		||||
to another harddisk using the
 | 
			
		||||
.Sy device Ar device-name
 | 
			
		||||
command.  You can view all devices with the
 | 
			
		||||
.Sy devices
 | 
			
		||||
command.
 | 
			
		||||
.Pp
 | 
			
		||||
If the current device does not already have a partition table, you can create a
 | 
			
		||||
.Xr mbr 7
 | 
			
		||||
or
 | 
			
		||||
.Xr gpt 7
 | 
			
		||||
partition table using the
 | 
			
		||||
.Sy mktable
 | 
			
		||||
command.
 | 
			
		||||
.Xr gpt 7
 | 
			
		||||
is the preferred choice for new partition tables as
 | 
			
		||||
.Xr mbr 7 has unfortunate limitations.
 | 
			
		||||
If you are dissatisfied with the current partition table, you can use
 | 
			
		||||
the
 | 
			
		||||
.Sy rmtable
 | 
			
		||||
command which will destroy the partition table and effectively delete all data
 | 
			
		||||
on the harddisk.
 | 
			
		||||
.Pp
 | 
			
		||||
The
 | 
			
		||||
.Sy ls
 | 
			
		||||
command to lists all partitions and unused space on the current device.
 | 
			
		||||
The
 | 
			
		||||
.Sy mkpart
 | 
			
		||||
command creates a partition.  You will be asked interactive questions to
 | 
			
		||||
determine its location.  You will be asked if you wish to format a filesystem.
 | 
			
		||||
.Nm ext2
 | 
			
		||||
is the native filesystem. If applicable, you will be asked if you wish to create
 | 
			
		||||
a mountpoint for it in
 | 
			
		||||
.Xr fstab 5 .
 | 
			
		||||
The
 | 
			
		||||
.Sy rmpart Ar partition-number
 | 
			
		||||
command removes a partition table entry and effectively deletes all data on the
 | 
			
		||||
partition.
 | 
			
		||||
.Pp
 | 
			
		||||
If the device containing the root filesystem uses the GPT partitioning scheme,
 | 
			
		||||
and you accepted the included bootloader, then you must create a
 | 
			
		||||
.Sy biosboot
 | 
			
		||||
partition onto which the bootloader is installed.  It should be at the start of
 | 
			
		||||
the harddisk and a size of 1 MiB will be more than sufficient.
 | 
			
		||||
.Pp
 | 
			
		||||
You need to make a partition containing the root filesystem mounted at
 | 
			
		||||
.Pa / .
 | 
			
		||||
A size of 1 GiB will be comfortable for the base system and ports and basic
 | 
			
		||||
usage.  There is no inherent need for a
 | 
			
		||||
.Pa /home
 | 
			
		||||
partition so you are encouraged to make the root filesystem as large as you
 | 
			
		||||
wish.  Operating systems upgrades will preserve the root filesystem and the
 | 
			
		||||
installer handles installing on top of an existing installation and preserves
 | 
			
		||||
user files and local configuration.
 | 
			
		||||
.Pp
 | 
			
		||||
Type
 | 
			
		||||
.Sy exit
 | 
			
		||||
when you are done to continue the installation.  If the installer detects a
 | 
			
		||||
problem with your partitioning, it will offer to run
 | 
			
		||||
.Xr disked 8
 | 
			
		||||
again.
 | 
			
		||||
.Ss Installation
 | 
			
		||||
The installer will show its installation intentions ask you to confirm the
 | 
			
		||||
installation.  If you answer yes, then the installation will begin.
 | 
			
		||||
.Pp
 | 
			
		||||
The installer will copy the live environment into the target root filesystem
 | 
			
		||||
according to the file lists in
 | 
			
		||||
.Pa /tix/manifest
 | 
			
		||||
and create configuration files matching your earlier choices.  It will generate
 | 
			
		||||
an initrd that locates and boots the root filesystem.  It will install the
 | 
			
		||||
bootloader if desired.  The installation will take a moment.
 | 
			
		||||
.Ss Configuration
 | 
			
		||||
After the installation is complete, a bare system is installed but it lacks
 | 
			
		||||
crucial configuration files and it will refuse to start when booted.
 | 
			
		||||
.Ss Hostname
 | 
			
		||||
You will be asked for the hostname of the new system which be stored in
 | 
			
		||||
.Xr hostname 5 .
 | 
			
		||||
This question is skipped if the file already exits.
 | 
			
		||||
.Ss Root
 | 
			
		||||
You will be asked for the root password.  A root account is made in
 | 
			
		||||
.Xr passwd 5
 | 
			
		||||
and
 | 
			
		||||
.Xr group 5 .
 | 
			
		||||
This question is skipped if the root account already exists.
 | 
			
		||||
.Ss Users
 | 
			
		||||
You will be asked in a loop if you wish to make another user.  Answer
 | 
			
		||||
.Sy no
 | 
			
		||||
when you are done.  Otherwise enter the name of the new account.  If you wish to
 | 
			
		||||
create an account by the name of
 | 
			
		||||
.Li no
 | 
			
		||||
then simply add a space in front as leading spaces are trimmed.
 | 
			
		||||
.Pp
 | 
			
		||||
You will then be asked for the full name and the password for the new user.  A
 | 
			
		||||
user directory will be made in
 | 
			
		||||
.Pa /home .
 | 
			
		||||
The new user is added to
 | 
			
		||||
.Xr passwd 5
 | 
			
		||||
and
 | 
			
		||||
.Xr group 5 .
 | 
			
		||||
.Pp
 | 
			
		||||
Please note that Sortix is not currently secure as a multi-user system and
 | 
			
		||||
filesystem permissions are not enforced.
 | 
			
		||||
.Ss Completion
 | 
			
		||||
This will complete the operating system installation. Upon reboot, the new
 | 
			
		||||
system will start normally. After powering off your system, you need to remove
 | 
			
		||||
the installation medium and if applicable restore boot priorities in your
 | 
			
		||||
firmware. If you did not accept the bootloader, you will need to manually
 | 
			
		||||
configure a bootloader to boot the new operating system.
 | 
			
		||||
.Pp
 | 
			
		||||
You will be given the choice between powering off the system, rebooting it, or
 | 
			
		||||
directly booting the new system. The last option will directly boot the new
 | 
			
		||||
system in a chroot while the live environment remains in the background. If you
 | 
			
		||||
invoked
 | 
			
		||||
.Xr sysinstall 8
 | 
			
		||||
yourself, then you will be returned to your live environment shell. Otherwise
 | 
			
		||||
the computer will power off when the chroot environment terminates.
 | 
			
		||||
.Pp
 | 
			
		||||
Upon boot of the new system it will be configured in multi-user mode and you
 | 
			
		||||
will be presented with a login screen. Authenticate as one of the local users
 | 
			
		||||
and you will be given a shell. To power off the computer login as user
 | 
			
		||||
.Sy poweroff
 | 
			
		||||
and to reboot the computer login as user
 | 
			
		||||
.Sy reboot .
 | 
			
		||||
.Pp
 | 
			
		||||
The
 | 
			
		||||
.Xr user-guide 7
 | 
			
		||||
manual page is a basic overview of the system for new users.
 | 
			
		||||
.Pp
 | 
			
		||||
Congratulations on your new Sortix system.
 | 
			
		||||
.Sh SEE ALSO
 | 
			
		||||
.Xr chkblayout 1 ,
 | 
			
		||||
.Xr chvideomode 1 ,
 | 
			
		||||
.Xr man 1 ,
 | 
			
		||||
.Xr fstab 5 ,
 | 
			
		||||
.Xr group 5 ,
 | 
			
		||||
.Xr grubpw 5 ,
 | 
			
		||||
.Xr kblayout 5 ,
 | 
			
		||||
.Xr passwd 5 ,
 | 
			
		||||
.Xr videomode 5 ,
 | 
			
		||||
.Xr development 7 ,
 | 
			
		||||
.Xr gpt 7 ,
 | 
			
		||||
.Xr initrd 7 ,
 | 
			
		||||
.Xr kernel 7 ,
 | 
			
		||||
.Xr mbr 7 ,
 | 
			
		||||
.Xr upgrade 7 ,
 | 
			
		||||
.Xr user-guide 7 ,
 | 
			
		||||
.Xr disked 8 ,
 | 
			
		||||
.Xr fsck 8 ,
 | 
			
		||||
.Xr init 8 ,
 | 
			
		||||
.Xr sysinstall 8 ,
 | 
			
		||||
.Xr sysupgrade 8 ,
 | 
			
		||||
.Xr update-grub 8 ,
 | 
			
		||||
.Xr update-initrd 8
 | 
			
		||||
							
								
								
									
										120
									
								
								share/man/man7/upgrade.7
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								share/man/man7/upgrade.7
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,120 @@
 | 
			
		|||
.Dd $Mdocdate: January 5 2016 $
 | 
			
		||||
.Dt UPGRADE 7
 | 
			
		||||
.Os
 | 
			
		||||
.Sh NAME
 | 
			
		||||
.Nm upgrade
 | 
			
		||||
.Nd operating system upgrade instructions
 | 
			
		||||
.Sh DESCRIPTION
 | 
			
		||||
This document describes how to upgrade an existing Sortix installation to a
 | 
			
		||||
newer release.  The
 | 
			
		||||
.Xr installation 7
 | 
			
		||||
manual page covers creating a new installation.
 | 
			
		||||
.Ss Prerequisites
 | 
			
		||||
You need to prepare a bootable medium with the new release as described in
 | 
			
		||||
.Xr installation 7 .
 | 
			
		||||
Read all the instructions up to Bootloader Menu and continue with this document.
 | 
			
		||||
.Pp
 | 
			
		||||
It is not possible to skip releases. If you want to upgrade to a release, you
 | 
			
		||||
must upgrade first to all releases before that one. If the existing installation
 | 
			
		||||
is a development snapshot, you must either upgrade to a newer development
 | 
			
		||||
snapshot of that version, or a the final release of that version.
 | 
			
		||||
.Ss Bootloader Menu
 | 
			
		||||
Pick the
 | 
			
		||||
.Li upgrade existing installation
 | 
			
		||||
option in the bootloader menu to begin the upgrade.  This will load a live
 | 
			
		||||
environment set to automatically run the
 | 
			
		||||
.Xr sysupgrade 8
 | 
			
		||||
program.
 | 
			
		||||
.Ss Upgrader
 | 
			
		||||
This guide assumes you selected the upgrade option in the bootloader.  If not,
 | 
			
		||||
you can run the installer by running the
 | 
			
		||||
.Xr sysupgrade 8
 | 
			
		||||
command.
 | 
			
		||||
.Pp
 | 
			
		||||
The upgrader is an interactive command line program that asks you questions and
 | 
			
		||||
you answer them.  It provides useful information you shouldn't accidentally
 | 
			
		||||
overlook.  Before answering any question, read all output since your last
 | 
			
		||||
answer.
 | 
			
		||||
.Pp
 | 
			
		||||
You should have this upgrade guide ready at all times.  You can view this
 | 
			
		||||
.Xr upgrade 7
 | 
			
		||||
page during the installation by answering
 | 
			
		||||
.Sy '!man'
 | 
			
		||||
to any regular prompt (excluding password prompts).  Likewise you can answer
 | 
			
		||||
.Sy '!'
 | 
			
		||||
to get an interactive shell.  Upon completion, you will be asked the question
 | 
			
		||||
again.
 | 
			
		||||
.Ss Keyboard Layout
 | 
			
		||||
You need to choose the applicable keyboard layout.  By default, a standard US
 | 
			
		||||
keyboard layout is used.  You can view a list of keyboard layouts if you wish.
 | 
			
		||||
.Ss Display Resolution
 | 
			
		||||
If a driver exists for your graphics card, then you will be asked for your
 | 
			
		||||
preferred display resolution by
 | 
			
		||||
.Xr chvideomode 1 .
 | 
			
		||||
.Ss Installation Search
 | 
			
		||||
The upgrader will search for existing Sortix installations by probing local
 | 
			
		||||
filesystems for
 | 
			
		||||
.Pa /etc/sortix-release .
 | 
			
		||||
You will be asked which installation you wish to upgrade.  Enter the name of the
 | 
			
		||||
root filesystem device.  If none are found, it asks if you want to run
 | 
			
		||||
.Xr sysinstall 8 .  Filesystems will be repaired by
 | 
			
		||||
.Xr fsck 8
 | 
			
		||||
as needed.
 | 
			
		||||
.Ss Confirmation
 | 
			
		||||
The upgrader will warn you if it detects you are not following proper upgrade
 | 
			
		||||
procedure either by downgrading or by skipping a release, neither of which is
 | 
			
		||||
supported.  It will also warn you if it detects an ABI downgrade.
 | 
			
		||||
.Pp
 | 
			
		||||
The upgrade will load the upgrade preferences from
 | 
			
		||||
.Pa /etc/upgrade.conf
 | 
			
		||||
file of the target system as described in
 | 
			
		||||
.Xr upgrade.conf 5 .
 | 
			
		||||
.Pp
 | 
			
		||||
The new release may have a new ABI.  A major ABI change means the new kernel
 | 
			
		||||
will be unable to properly execute old programs.  A minor ABI change means the
 | 
			
		||||
new kernel has new compatible features and will be able to run older programs,
 | 
			
		||||
but older kernels will not be able to run programs using the new ABI.
 | 
			
		||||
.Pp
 | 
			
		||||
A confirmation screen will tell you what actions the upgrader plan on doing.
 | 
			
		||||
Answer
 | 
			
		||||
.Sy yes
 | 
			
		||||
to proceed with the upgrade.  Otherwise you can escape to a shell, edit
 | 
			
		||||
.Xr upgrade.conf 5 ,
 | 
			
		||||
return and answer
 | 
			
		||||
.Sy no
 | 
			
		||||
and the upgrader will reload the configuration.
 | 
			
		||||
.Ss Upgrade
 | 
			
		||||
The upgrader will take the appropriate actions:
 | 
			
		||||
.Pp
 | 
			
		||||
.Bl -bullet -compact
 | 
			
		||||
.It
 | 
			
		||||
Updating the system.
 | 
			
		||||
.It
 | 
			
		||||
Updating the ports.
 | 
			
		||||
.It
 | 
			
		||||
Updating the source code.
 | 
			
		||||
.It
 | 
			
		||||
Updating the initrd.
 | 
			
		||||
.It
 | 
			
		||||
Updating the bootloader.
 | 
			
		||||
.El
 | 
			
		||||
.Ss Completion
 | 
			
		||||
The upgrade is now complete. The new system will run after a reboot.  The
 | 
			
		||||
upgrader will give you an overview of what it has done.  If you upgraded across
 | 
			
		||||
a major ABI change, then you will be told that you need to recompile all local
 | 
			
		||||
programs to use the new ABI.
 | 
			
		||||
.Pp
 | 
			
		||||
Congratulations on your freshly upgraded Sortix system.
 | 
			
		||||
.Sh SEE ALSO
 | 
			
		||||
.Xr chkblayout 1 ,
 | 
			
		||||
.Xr chvideomode 1 ,
 | 
			
		||||
.Xr man 1 ,
 | 
			
		||||
.Xr development 7 ,
 | 
			
		||||
.Xr initrd 7 ,
 | 
			
		||||
.Xr installation 7 ,
 | 
			
		||||
.Xr kernel 7 ,
 | 
			
		||||
.Xr user-guide 7 ,
 | 
			
		||||
.Xr sysinstall 8 ,
 | 
			
		||||
.Xr sysupgrade 8 ,
 | 
			
		||||
.Xr update-grub 8 ,
 | 
			
		||||
.Xr update-initrd 8
 | 
			
		||||
| 
						 | 
				
			
			@ -9,8 +9,13 @@ Sortix is a small self-hosting Unix-like operating system developed since 2011.
 | 
			
		|||
This document covers matters relevant to new users from other Unix-like
 | 
			
		||||
operating systems.
 | 
			
		||||
.Ss Introduction
 | 
			
		||||
You will be presented a with standard Unix command line environment upon booting
 | 
			
		||||
the live environment.
 | 
			
		||||
The installation process is covered in
 | 
			
		||||
.Xr installation 7 .
 | 
			
		||||
Bootable cdrom releases will offer the options of running a live environment,
 | 
			
		||||
installing the operating system, or upgrading an existing installation.
 | 
			
		||||
.Pp
 | 
			
		||||
You will be presented a with standard Unix command line environment upon login or
 | 
			
		||||
booting the live environment.
 | 
			
		||||
.Ss Shutdown
 | 
			
		||||
.Xr init 8
 | 
			
		||||
spawns a session after boot.  This is
 | 
			
		||||
| 
						 | 
				
			
			@ -125,4 +130,6 @@ but it is becoming feasible to build a large number of them natively.
 | 
			
		|||
.Sh SEE ALSO
 | 
			
		||||
.Xr cross-development 7 ,
 | 
			
		||||
.Xr development 7 ,
 | 
			
		||||
.Xr serial-transfer 7
 | 
			
		||||
.Xr installation 7 ,
 | 
			
		||||
.Xr serial-transfer 7 ,
 | 
			
		||||
.Xr upgrade 7
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										4
									
								
								sysinstall/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								sysinstall/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
sysinstall
 | 
			
		||||
sysmerge
 | 
			
		||||
sysupgrade
 | 
			
		||||
*.o
 | 
			
		||||
							
								
								
									
										75
									
								
								sysinstall/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								sysinstall/Makefile
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,75 @@
 | 
			
		|||
SOFTWARE_MEANT_FOR_SORTIX=1
 | 
			
		||||
include ../build-aux/platform.mak
 | 
			
		||||
include ../build-aux/compiler.mak
 | 
			
		||||
include ../build-aux/version.mak
 | 
			
		||||
include ../build-aux/dirs.mak
 | 
			
		||||
 | 
			
		||||
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
 | 
			
		||||
CFLAGS?=$(OPTLEVEL)
 | 
			
		||||
 | 
			
		||||
CFLAGS:=$(CFLAGS) -Wall -Wextra
 | 
			
		||||
CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\"
 | 
			
		||||
 | 
			
		||||
MAIN_OBJS=\
 | 
			
		||||
sysinstall.o \
 | 
			
		||||
sysmerge.o \
 | 
			
		||||
sysupgrade.o \
 | 
			
		||||
 | 
			
		||||
UTIL_OBJS=\
 | 
			
		||||
conf.o \
 | 
			
		||||
devices.o \
 | 
			
		||||
execute.o \
 | 
			
		||||
fileops.o \
 | 
			
		||||
interactive.o \
 | 
			
		||||
manifest.o \
 | 
			
		||||
release.o \
 | 
			
		||||
 | 
			
		||||
OBJS=$(MAIN_OBJS) $(UTIL_OBJS)
 | 
			
		||||
 | 
			
		||||
SYSINSTALL_DEPS=devices execute fileops interactive manifest
 | 
			
		||||
SYSMERGE_DEPS=conf fileops execute manifest release
 | 
			
		||||
SYSUPGRADE_DEPS=conf devices execute fileops interactive manifest release
 | 
			
		||||
 | 
			
		||||
SYSINSTALL_OBJS:=sysinstall.o $(SYSINSTALL_DEPS:=.o)
 | 
			
		||||
SYSMERGE_OBJS:=sysmerge.o $(SYSMERGE_DEPS:=.o)
 | 
			
		||||
SYSUPGRADE_OBJS:=sysupgrade.o $(SYSUPGRADE_DEPS:=.o)
 | 
			
		||||
 | 
			
		||||
all: sysinstall sysmerge sysupgrade
 | 
			
		||||
 | 
			
		||||
.PHONY: all install clean
 | 
			
		||||
 | 
			
		||||
install: all
 | 
			
		||||
	mkdir -p $(DESTDIR)$(SBINDIR)
 | 
			
		||||
	install sysinstall $(DESTDIR)$(SBINDIR)
 | 
			
		||||
	install sysmerge $(DESTDIR)$(SBINDIR)
 | 
			
		||||
	install sysupgrade $(DESTDIR)$(SBINDIR)
 | 
			
		||||
	mkdir -p $(DESTDIR)$(MANDIR)/man8
 | 
			
		||||
	install sysinstall.8 $(DESTDIR)$(MANDIR)/man8/sysinstall.8
 | 
			
		||||
	install sysmerge.8 $(DESTDIR)$(MANDIR)/man8/sysmerge.8
 | 
			
		||||
	install sysupgrade.8 $(DESTDIR)$(MANDIR)/man8/sysupgrade.8
 | 
			
		||||
 | 
			
		||||
sysinstall: $(SYSINSTALL_OBJS)
 | 
			
		||||
	$(CC) $(SYSINSTALL_OBJS) -o $@ -lmount
 | 
			
		||||
 | 
			
		||||
sysmerge: $(SYSMERGE_OBJS)
 | 
			
		||||
	$(CC) $(SYSMERGE_OBJS) -o $@ -lmount
 | 
			
		||||
 | 
			
		||||
sysupgrade: $(SYSUPGRADE_OBJS)
 | 
			
		||||
	$(CC) $(SYSUPGRADE_OBJS) -o $@ -lmount
 | 
			
		||||
 | 
			
		||||
%.o: %.c
 | 
			
		||||
	$(CC) $(CFLAGS) $(CPPFLAGS) -std=gnu11 -c $< -o $@
 | 
			
		||||
 | 
			
		||||
sysinstall.o: $(SYSINSTALL_DEPS:=.h)
 | 
			
		||||
sysmerge.o: $(SYSMERGE_DEPS:=.h)
 | 
			
		||||
sysupgrade.o: $(SYSUPGRADE_DEPS:=.h)
 | 
			
		||||
conf.o: conf.h
 | 
			
		||||
devices.o: devices.h
 | 
			
		||||
execute.o: execute.h
 | 
			
		||||
fileops.o: fileops.h
 | 
			
		||||
interactive.o: interactive.h execute.h
 | 
			
		||||
manifest.o: manifest.h execute.h fileops.h
 | 
			
		||||
release.o: release.h
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f sysinstall sysmerge sysupgrade $(OBJS)
 | 
			
		||||
							
								
								
									
										115
									
								
								sysinstall/conf.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								sysinstall/conf.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,115 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    conf.c
 | 
			
		||||
    Utility functions to handle upgrade.conf(5).
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <err.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "conf.h"
 | 
			
		||||
 | 
			
		||||
static bool conf_boolean(const char* name, const char* value, const char* path)
 | 
			
		||||
{
 | 
			
		||||
	if ( !strcmp(value, "yes") )
 | 
			
		||||
		return true;
 | 
			
		||||
	if ( !strcmp(value, "no") )
 | 
			
		||||
		return false;
 | 
			
		||||
	printf("%s: %s: Expected yes or no instead of unsupported value\n",
 | 
			
		||||
	       path, name);
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void conf_assign(struct conf* conf,
 | 
			
		||||
                        const char* name,
 | 
			
		||||
                        const char* value,
 | 
			
		||||
                        const char* path)
 | 
			
		||||
{
 | 
			
		||||
	if ( !strcmp(name, "grub") )
 | 
			
		||||
		conf->grub = conf_boolean(name, value, path);
 | 
			
		||||
	else if ( !strcmp(name, "newsrc") )
 | 
			
		||||
		conf->newsrc = conf_boolean(name, value, path);
 | 
			
		||||
	else if ( !strcmp(name, "ports") )
 | 
			
		||||
		conf->ports = conf_boolean(name, value, path);
 | 
			
		||||
	else if ( !strcmp(name, "src") )
 | 
			
		||||
		conf->src = conf_boolean(name, value, path);
 | 
			
		||||
	else if ( !strcmp(name, "system") )
 | 
			
		||||
		conf->system = conf_boolean(name, value, path);
 | 
			
		||||
	else
 | 
			
		||||
		printf("%s: %s: Unsupported variable\n", path, name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void load_upgrade_conf(struct conf* conf, const char* path)
 | 
			
		||||
{
 | 
			
		||||
	memset(conf, 0, sizeof(*conf));
 | 
			
		||||
	conf->ports = true;
 | 
			
		||||
	conf->system = true;
 | 
			
		||||
 | 
			
		||||
	FILE* fp = fopen(path, "r");
 | 
			
		||||
	if ( !fp )
 | 
			
		||||
	{
 | 
			
		||||
		if ( errno == ENOENT )
 | 
			
		||||
			return;
 | 
			
		||||
		err(2, "%s", path);
 | 
			
		||||
	}
 | 
			
		||||
	char* line = NULL;
 | 
			
		||||
	size_t line_size = 0;
 | 
			
		||||
	ssize_t line_length;
 | 
			
		||||
	while ( 0 < (errno = 0, line_length = getline(&line, &line_size, fp)) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( line[line_length - 1] == '\n' )
 | 
			
		||||
			line[--line_length] = '\0';
 | 
			
		||||
		line_length = 0;
 | 
			
		||||
		while ( line[line_length] && line[line_length] != '#' )
 | 
			
		||||
			line_length++;
 | 
			
		||||
		line[line_length] = '\0';
 | 
			
		||||
		while ( line_length && isblank((unsigned char) line[line_length - 1]) )
 | 
			
		||||
			line[--line_length] = '\0';
 | 
			
		||||
		char* name = line;
 | 
			
		||||
		while ( *name && isblank((unsigned char) *name) )
 | 
			
		||||
			name++;
 | 
			
		||||
		if ( !*name || *name == '=' )
 | 
			
		||||
			continue;
 | 
			
		||||
		size_t name_length = 1;
 | 
			
		||||
		while ( name[name_length] &&
 | 
			
		||||
		        !isblank((unsigned char) name[name_length]) &&
 | 
			
		||||
		        name[name_length] != '=' )
 | 
			
		||||
			name_length++;
 | 
			
		||||
		char* value = name + name_length;
 | 
			
		||||
		while ( *value && isblank((unsigned char) *value) )
 | 
			
		||||
			value++;
 | 
			
		||||
		if ( *value != '=' )
 | 
			
		||||
			continue;
 | 
			
		||||
		value++;
 | 
			
		||||
		while ( *value && isblank((unsigned char) *value) )
 | 
			
		||||
			value++;
 | 
			
		||||
		name[name_length] = '\0';
 | 
			
		||||
		conf_assign(conf, name, value, path);
 | 
			
		||||
	}
 | 
			
		||||
	if ( errno )
 | 
			
		||||
		err(2, "%s", path);
 | 
			
		||||
	free(line);
 | 
			
		||||
	fclose(fp);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								sysinstall/conf.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								sysinstall/conf.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    conf.h
 | 
			
		||||
    Utility functions to handle upgrade.conf(5).
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef CONF_H
 | 
			
		||||
#define CONF_H
 | 
			
		||||
 | 
			
		||||
struct conf
 | 
			
		||||
{
 | 
			
		||||
	bool grub;
 | 
			
		||||
	bool newsrc;
 | 
			
		||||
	bool ports;
 | 
			
		||||
	bool src;
 | 
			
		||||
	bool system;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void load_upgrade_conf(struct conf* conf, const char* path);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										416
									
								
								sysinstall/devices.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										416
									
								
								sysinstall/devices.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,416 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    devices.c
 | 
			
		||||
    Utility functions to handle devices, partitions, and filesystems.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <sys/mount.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <err.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fstab.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <timespec.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include <mount/blockdevice.h>
 | 
			
		||||
#include <mount/devices.h>
 | 
			
		||||
#include <mount/filesystem.h>
 | 
			
		||||
#include <mount/harddisk.h>
 | 
			
		||||
#include <mount/partition.h>
 | 
			
		||||
#include <mount/uuid.h>
 | 
			
		||||
 | 
			
		||||
#include "devices.h"
 | 
			
		||||
 | 
			
		||||
struct harddisk** hds;
 | 
			
		||||
size_t hds_count;
 | 
			
		||||
 | 
			
		||||
const char* path_of_blockdevice(struct blockdevice* bdev)
 | 
			
		||||
{
 | 
			
		||||
	return bdev->p ? bdev->p->path : bdev->hd->path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* device_path_of_blockdevice(struct blockdevice* bdev)
 | 
			
		||||
{
 | 
			
		||||
	while ( bdev->p )
 | 
			
		||||
		bdev = bdev->p->parent_bdev;
 | 
			
		||||
	return bdev->hd->path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void unscan_filesystem(struct blockdevice* bdev)
 | 
			
		||||
{
 | 
			
		||||
	if ( bdev->fs )
 | 
			
		||||
	{
 | 
			
		||||
		filesystem_release(bdev->fs);
 | 
			
		||||
		bdev->fs = NULL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void scan_filesystem(struct blockdevice* bdev)
 | 
			
		||||
{
 | 
			
		||||
	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; // TODO: Perhaps print an error here?
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void unscan_device(struct harddisk* hd)
 | 
			
		||||
{
 | 
			
		||||
	if ( hd->bdev.pt )
 | 
			
		||||
	{
 | 
			
		||||
		for ( size_t i = 0; i < hd->bdev.pt->partitions_count; i++ )
 | 
			
		||||
			unscan_filesystem(&hd->bdev.pt->partitions[i]->bdev);
 | 
			
		||||
		partition_table_release(hd->bdev.pt);
 | 
			
		||||
		hd->bdev.pt = NULL;
 | 
			
		||||
	}
 | 
			
		||||
	if ( hd->bdev.fs )
 | 
			
		||||
		unscan_filesystem(&hd->bdev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void scan_device(struct harddisk* hd)
 | 
			
		||||
{
 | 
			
		||||
	unscan_device(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 )
 | 
			
		||||
	{
 | 
			
		||||
		scan_filesystem(bdev);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	else if ( parterr == PARTITION_ERROR_ERRNO ||
 | 
			
		||||
	          parterr != PARTITION_ERROR_NONE )
 | 
			
		||||
		return; // TODO: Perhaps print an error here?
 | 
			
		||||
	for ( size_t i = 0; i < bdev->pt->partitions_count; i++ )
 | 
			
		||||
		scan_filesystem(&bdev->pt->partitions[i]->bdev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void unscan_devices(void)
 | 
			
		||||
{
 | 
			
		||||
	for ( size_t i = 0; i < hds_count; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		unscan_device(hds[i]);
 | 
			
		||||
		harddisk_close(hds[i]);
 | 
			
		||||
	}
 | 
			
		||||
	hds_count = 0;
 | 
			
		||||
	free(hds);
 | 
			
		||||
	hds = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void scan_devices(void)
 | 
			
		||||
{
 | 
			
		||||
	unscan_devices();
 | 
			
		||||
	if ( !devices_open_all(&hds, &hds_count) )
 | 
			
		||||
	{
 | 
			
		||||
		// TODO: How should callers deal with error conditions from here?
 | 
			
		||||
		warn("iterating devices");
 | 
			
		||||
	}
 | 
			
		||||
	for ( size_t i = 0; i < hds_count; i++ )
 | 
			
		||||
		scan_device(hds[i]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct filesystem* search_for_filesystem_by_uuid(const unsigned char* uuid)
 | 
			
		||||
{
 | 
			
		||||
	for ( size_t di = 0; di < hds_count; di++ )
 | 
			
		||||
	{
 | 
			
		||||
		struct blockdevice* dbdev = &hds[di]->bdev;
 | 
			
		||||
		if ( dbdev->fs )
 | 
			
		||||
		{
 | 
			
		||||
			if ( (dbdev->fs->flags & FILESYSTEM_FLAG_UUID) &&
 | 
			
		||||
			     memcmp(dbdev->fs->uuid, uuid, 16) == 0 )
 | 
			
		||||
				return dbdev->fs;
 | 
			
		||||
		}
 | 
			
		||||
		else if ( dbdev->pt )
 | 
			
		||||
		{
 | 
			
		||||
			for ( size_t pi = 0; pi < dbdev->pt->partitions_count; pi++ )
 | 
			
		||||
			{
 | 
			
		||||
				struct blockdevice* pbdev = &dbdev->pt->partitions[pi]->bdev;
 | 
			
		||||
				if ( !pbdev->fs )
 | 
			
		||||
					continue;
 | 
			
		||||
				if ( (pbdev->fs->flags & FILESYSTEM_FLAG_UUID) &&
 | 
			
		||||
					 memcmp(pbdev->fs->uuid, uuid, 16) == 0 )
 | 
			
		||||
					return pbdev->fs;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct filesystem* search_for_filesystem_by_spec(const char* spec)
 | 
			
		||||
{
 | 
			
		||||
	if ( strncmp(spec, "UUID=", strlen("UUID=")) == 0 )
 | 
			
		||||
	{
 | 
			
		||||
		const char* uuid_string = spec + strlen("UUID=");
 | 
			
		||||
		if ( !uuid_validate(uuid_string) )
 | 
			
		||||
			return NULL;
 | 
			
		||||
		unsigned char uuid[16];
 | 
			
		||||
		uuid_from_string(uuid, uuid_string);
 | 
			
		||||
		return search_for_filesystem_by_uuid(uuid);
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool check_existing_systems(void)
 | 
			
		||||
{
 | 
			
		||||
	for ( size_t di = 0; di < hds_count; di++ )
 | 
			
		||||
	{
 | 
			
		||||
		struct blockdevice* dbdev = &hds[di]->bdev;
 | 
			
		||||
		if ( dbdev->fs )
 | 
			
		||||
			return true;
 | 
			
		||||
		else if ( dbdev->pt )
 | 
			
		||||
			return 1 <= dbdev->pt->partitions_count;
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool check_lacking_partition_table(void)
 | 
			
		||||
{
 | 
			
		||||
	for ( size_t di = 0; di < hds_count; di++ )
 | 
			
		||||
	{
 | 
			
		||||
		struct blockdevice* dbdev = &hds[di]->bdev;
 | 
			
		||||
		if ( dbdev->fs )
 | 
			
		||||
			continue;
 | 
			
		||||
		if ( !dbdev->pt )
 | 
			
		||||
			return true;
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool fsck(struct filesystem* fs)
 | 
			
		||||
{
 | 
			
		||||
	const char* bdev_path = path_of_blockdevice(fs->bdev);
 | 
			
		||||
	printf("%s: Repairing filesystem due to inconsistency...\n", bdev_path);
 | 
			
		||||
	assert(fs->fsck);
 | 
			
		||||
	pid_t child_pid = fork();
 | 
			
		||||
	if ( child_pid < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		warn("%s: Mandatory repair failed: fork", bdev_path);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	if ( child_pid == 0 )
 | 
			
		||||
	{
 | 
			
		||||
		execlp(fs->fsck, fs->fsck, "-fp", "--", bdev_path, (const char*) NULL);
 | 
			
		||||
		warn("%s: Failed to load filesystem checker: %s", bdev_path, fs->fsck);
 | 
			
		||||
		_Exit(127);
 | 
			
		||||
	}
 | 
			
		||||
	int code;
 | 
			
		||||
	if ( waitpid(child_pid, &code, 0) < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		warn("waitpid");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	if ( WIFSIGNALED(code) )
 | 
			
		||||
		warnx("%s: Mandatory repair failed: %s: %s", bdev_path,
 | 
			
		||||
		      fs->fsck, strsignal(WTERMSIG(code)));
 | 
			
		||||
	else if ( !WIFEXITED(code) )
 | 
			
		||||
		warnx("%s: Mandatory repair failed: %s: %s", bdev_path,
 | 
			
		||||
		      fs->fsck, "Unexpected unusual termination");
 | 
			
		||||
	else if ( WEXITSTATUS(code) == 127 )
 | 
			
		||||
		warnx("%s: Mandatory repair failed: %s: %s", bdev_path,
 | 
			
		||||
		      fs->fsck, "Filesystem checker is absent");
 | 
			
		||||
	else if ( WEXITSTATUS(code) & 2 )
 | 
			
		||||
		warnx("%s: Mandatory repair: %s: %s", bdev_path,
 | 
			
		||||
		      fs->fsck, "System reboot is necessary");
 | 
			
		||||
	else if ( WEXITSTATUS(code) != 0 && WEXITSTATUS(code) != 1 )
 | 
			
		||||
		warnx("%s: Mandatory repair failed: %s: %s", bdev_path,
 | 
			
		||||
		      fs->fsck, "Filesystem checker was unsuccessful");
 | 
			
		||||
	else
 | 
			
		||||
		return true;
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int sort_mountpoint(const void* a_ptr, const void* b_ptr)
 | 
			
		||||
{
 | 
			
		||||
	const struct mountpoint* a = (const struct mountpoint*) a_ptr;
 | 
			
		||||
	const struct mountpoint* b = (const struct mountpoint*) b_ptr;
 | 
			
		||||
	return strcmp(a->entry.fs_file, b->entry.fs_file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void free_mountpoints(struct mountpoint* mnts, size_t mnts_count)
 | 
			
		||||
{
 | 
			
		||||
	for ( size_t i = 0; i < mnts_count; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		free(mnts[i].entry_line);
 | 
			
		||||
		free(mnts[i].absolute);
 | 
			
		||||
	}
 | 
			
		||||
	free(mnts);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool load_mountpoints(const char* fstab_path,
 | 
			
		||||
                      struct mountpoint** mountpoints_out,
 | 
			
		||||
                      size_t* mountpoints_used_out)
 | 
			
		||||
{
 | 
			
		||||
	FILE* fp = fopen(fstab_path, "r");
 | 
			
		||||
	if ( !fp )
 | 
			
		||||
		return false;
 | 
			
		||||
	struct mountpoint* mountpoints = NULL;
 | 
			
		||||
	size_t mountpoints_used = 0;
 | 
			
		||||
	size_t mountpoints_length = 0;
 | 
			
		||||
	char* line = NULL;
 | 
			
		||||
	size_t line_size;
 | 
			
		||||
	ssize_t line_length;
 | 
			
		||||
	while ( 0 < (errno = 0, line_length = getline(&line, &line_size, fp)) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( line[line_length - 1] == '\n' )
 | 
			
		||||
			line[--line_length] = '\0';
 | 
			
		||||
		struct fstab fstabent;
 | 
			
		||||
		if ( !scanfsent(line, &fstabent) )
 | 
			
		||||
			continue;
 | 
			
		||||
		if ( mountpoints_used == mountpoints_length )
 | 
			
		||||
		{
 | 
			
		||||
			size_t new_length = 2 * mountpoints_length;
 | 
			
		||||
			if ( !new_length )
 | 
			
		||||
				new_length = 16;
 | 
			
		||||
			struct mountpoint* new_mountpoints = (struct mountpoint*)
 | 
			
		||||
				reallocarray(mountpoints, new_length, sizeof(struct mountpoint));
 | 
			
		||||
			if ( !new_mountpoints )
 | 
			
		||||
			{
 | 
			
		||||
				free_mountpoints(mountpoints, mountpoints_used);
 | 
			
		||||
				free(line);
 | 
			
		||||
				fclose(fp);
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
			mountpoints = new_mountpoints;
 | 
			
		||||
			mountpoints_length = new_length;
 | 
			
		||||
		}
 | 
			
		||||
		struct mountpoint* mountpoint = &mountpoints[mountpoints_used++];
 | 
			
		||||
		memcpy(&mountpoint->entry, &fstabent, sizeof(fstabent));
 | 
			
		||||
		mountpoint->entry_line = line;
 | 
			
		||||
		mountpoint->pid = -1;
 | 
			
		||||
		if ( !(mountpoint->absolute = strdup(mountpoint->entry.fs_file)) )
 | 
			
		||||
		{
 | 
			
		||||
			free_mountpoints(mountpoints, mountpoints_used);
 | 
			
		||||
			fclose(fp);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		line = NULL;
 | 
			
		||||
		line_size = 0;
 | 
			
		||||
	}
 | 
			
		||||
	bool failure = errno;
 | 
			
		||||
	free(line);
 | 
			
		||||
	fclose(fp);
 | 
			
		||||
	if ( failure )
 | 
			
		||||
	{
 | 
			
		||||
		free_mountpoints(mountpoints, mountpoints_used);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	qsort(mountpoints, mountpoints_used, sizeof(struct mountpoint),
 | 
			
		||||
	      sort_mountpoint);
 | 
			
		||||
	*mountpoints_out = mountpoints;
 | 
			
		||||
	*mountpoints_used_out = mountpoints_used;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mountpoint_mount(struct mountpoint* mountpoint)
 | 
			
		||||
{
 | 
			
		||||
	struct filesystem* fs = mountpoint->fs;
 | 
			
		||||
	// 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);
 | 
			
		||||
	if ( fs->flags & FILESYSTEM_FLAG_FSCK_MUST && !fsck(fs) )
 | 
			
		||||
		errx(2, "Failed to fsck %s", bdev_path);
 | 
			
		||||
	if ( !fs->driver )
 | 
			
		||||
		errx(2, "%s: Don't know how to mount a %s filesystem",
 | 
			
		||||
		     bdev_path, fs->fstype_name);
 | 
			
		||||
	const char* pretend_where = mountpoint->entry.fs_file;
 | 
			
		||||
	const char* where = mountpoint->absolute;
 | 
			
		||||
	struct stat st;
 | 
			
		||||
	if ( stat(where, &st) < 0 )
 | 
			
		||||
		err(2, "stat: %s", where);
 | 
			
		||||
	if ( (mountpoint->pid = fork()) < 0 )
 | 
			
		||||
		err(2, "%s: Unable to mount: fork", bdev_path);
 | 
			
		||||
	// TODO: This design is broken. The filesystem should tell us when it is
 | 
			
		||||
	//       ready instead of having to poll like this.
 | 
			
		||||
	if ( mountpoint->pid == 0 )
 | 
			
		||||
	{
 | 
			
		||||
		execlp(fs->driver, fs->driver, "--foreground", bdev_path, where,
 | 
			
		||||
		       "--pretend-mount-path", pretend_where, (const char*) NULL);
 | 
			
		||||
		warn("%s: Failed to load filesystem driver: %s", bdev_path, fs->driver);
 | 
			
		||||
		_exit(127);
 | 
			
		||||
	}
 | 
			
		||||
	while ( true )
 | 
			
		||||
	{
 | 
			
		||||
		struct stat newst;
 | 
			
		||||
		if ( stat(where, &newst) < 0 )
 | 
			
		||||
		{
 | 
			
		||||
			warn("stat: %s", where);
 | 
			
		||||
			if ( unmount(where, 0) < 0 && errno != ENOMOUNT )
 | 
			
		||||
				warn("unmount: %s", where);
 | 
			
		||||
			else if ( errno == ENOMOUNT )
 | 
			
		||||
				kill(mountpoint->pid, SIGQUIT);
 | 
			
		||||
			int code;
 | 
			
		||||
			waitpid(mountpoint->pid, &code, 0);
 | 
			
		||||
			mountpoint->pid = -1;
 | 
			
		||||
			exit(2);
 | 
			
		||||
		}
 | 
			
		||||
		if ( newst.st_dev != st.st_dev || newst.st_ino != st.st_ino )
 | 
			
		||||
			break;
 | 
			
		||||
		int code;
 | 
			
		||||
		pid_t child = waitpid(mountpoint->pid, &code, WNOHANG);
 | 
			
		||||
		if ( child < 0 )
 | 
			
		||||
			err(2, "waitpid");
 | 
			
		||||
		if ( child != 0 )
 | 
			
		||||
		{
 | 
			
		||||
			mountpoint->pid = -1;
 | 
			
		||||
			if ( WIFSIGNALED(code) )
 | 
			
		||||
				err(2, "%s: Mount failed: %s: %s", bdev_path, fs->driver,
 | 
			
		||||
				       strsignal(WTERMSIG(code)));
 | 
			
		||||
			else if ( !WIFEXITED(code) )
 | 
			
		||||
				err(2, "%s: Mount failed: %s: %s", bdev_path, fs->driver,
 | 
			
		||||
				       "Unexpected unusual termination");
 | 
			
		||||
			else if ( WEXITSTATUS(code) == 127 )
 | 
			
		||||
				err(2, "%s: Mount failed: %s: %s", bdev_path, fs->driver,
 | 
			
		||||
				       "Filesystem driver is absent");
 | 
			
		||||
			else if ( WEXITSTATUS(code) == 0 )
 | 
			
		||||
				err(2, "%s: Mount failed: %s: Unexpected successful exit",
 | 
			
		||||
				       bdev_path, fs->driver);
 | 
			
		||||
			else
 | 
			
		||||
				err(2, "%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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mountpoint_unmount(struct mountpoint* mountpoint)
 | 
			
		||||
{
 | 
			
		||||
	if ( mountpoint->pid < 0 )
 | 
			
		||||
		return;
 | 
			
		||||
	if ( unmount(mountpoint->absolute, 0) < 0 && errno != ENOMOUNT )
 | 
			
		||||
		warn("unmount: %s", mountpoint->entry.fs_file);
 | 
			
		||||
	else if ( errno == ENOMOUNT )
 | 
			
		||||
		kill(mountpoint->pid, SIGQUIT);
 | 
			
		||||
	int code;
 | 
			
		||||
	if ( waitpid(mountpoint->pid, &code, 0) < 0 )
 | 
			
		||||
		warn("waitpid");
 | 
			
		||||
	mountpoint->pid = -1;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										58
									
								
								sysinstall/devices.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								sysinstall/devices.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    devices.h
 | 
			
		||||
    Utility functions to handle devices, partitions, and filesystems.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef DEVICES_H
 | 
			
		||||
#define DEVICES_H
 | 
			
		||||
 | 
			
		||||
struct mountpoint
 | 
			
		||||
{
 | 
			
		||||
	struct fstab entry;
 | 
			
		||||
	char* entry_line;
 | 
			
		||||
	pid_t pid;
 | 
			
		||||
	char* absolute;
 | 
			
		||||
	struct filesystem* fs;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct harddisk** hds;
 | 
			
		||||
extern size_t hds_count;
 | 
			
		||||
 | 
			
		||||
const char* path_of_blockdevice(struct blockdevice* bdev);
 | 
			
		||||
const char* device_path_of_blockdevice(struct blockdevice* bdev);
 | 
			
		||||
void unscan_filesystem(struct blockdevice* bdev);
 | 
			
		||||
void scan_filesystem(struct blockdevice* bdev);
 | 
			
		||||
void unscan_device(struct harddisk* hd);
 | 
			
		||||
void scan_device(struct harddisk* hd);
 | 
			
		||||
void unscan_devices(void);
 | 
			
		||||
void scan_devices(void);
 | 
			
		||||
struct filesystem* search_for_filesystem_by_uuid(const unsigned char* uuid);
 | 
			
		||||
struct filesystem* search_for_filesystem_by_spec(const char* spec);
 | 
			
		||||
bool check_existing_systems(void);
 | 
			
		||||
bool check_lacking_partition_table(void);
 | 
			
		||||
bool fsck(struct filesystem* fs);
 | 
			
		||||
void free_mountpoints(struct mountpoint* mnts, size_t mnts_count);
 | 
			
		||||
bool load_mountpoints(const char* fstab_path,
 | 
			
		||||
                      struct mountpoint** mountpoints_out,
 | 
			
		||||
                      size_t* mountpoints_used_out);
 | 
			
		||||
void mountpoint_mount(struct mountpoint* mountpoint);
 | 
			
		||||
void mountpoint_unmount(struct mountpoint* mountpoint);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										131
									
								
								sysinstall/execute.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								sysinstall/execute.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,131 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    execute.h
 | 
			
		||||
    Template for common execvp use cases.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#include <err.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
 | 
			
		||||
int execute(const char* const* argv, const char* flags, ...)
 | 
			
		||||
{
 | 
			
		||||
	bool _exit_instead = false;
 | 
			
		||||
	bool exit_on_failure = false;
 | 
			
		||||
	bool foreground = false;
 | 
			
		||||
	bool gid_set = false;
 | 
			
		||||
	bool raw_exit_code = false;
 | 
			
		||||
	bool uid_set = false;
 | 
			
		||||
	bool quiet = false;
 | 
			
		||||
	bool quiet_stderr = false;
 | 
			
		||||
	gid_t gid = 0;
 | 
			
		||||
	uid_t uid = 0;
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	va_start(ap, flags);
 | 
			
		||||
	for ( size_t i = 0; flags[i]; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		switch ( flags[i] )
 | 
			
		||||
		{
 | 
			
		||||
		case '_': _exit_instead = true; break;
 | 
			
		||||
		case 'e': exit_on_failure = true; break;
 | 
			
		||||
		case 'f': foreground = true; break;
 | 
			
		||||
		case 'g': gid_set = true; gid = va_arg(ap, gid_t); break;
 | 
			
		||||
		case 'r': raw_exit_code = true; break;
 | 
			
		||||
		case 'u': uid_set = true; uid = va_arg(ap, uid_t); break;
 | 
			
		||||
		case 'q': quiet = true; break;
 | 
			
		||||
		case 'Q': quiet_stderr = true; break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
	sigset_t oldset, sigttou;
 | 
			
		||||
	if ( foreground )
 | 
			
		||||
	{
 | 
			
		||||
		sigemptyset(&sigttou);
 | 
			
		||||
		sigaddset(&sigttou, SIGTTOU);
 | 
			
		||||
	}
 | 
			
		||||
	pid_t child_pid = fork();
 | 
			
		||||
	if ( child_pid < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		warn("fork");
 | 
			
		||||
		if ( exit_on_failure )
 | 
			
		||||
			(_exit_instead ? _exit : exit)(2);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	if ( child_pid == 0 )
 | 
			
		||||
	{
 | 
			
		||||
		if ( gid_set )
 | 
			
		||||
		{
 | 
			
		||||
			setegid(gid);
 | 
			
		||||
			setgid(gid);
 | 
			
		||||
		}
 | 
			
		||||
		if ( uid_set )
 | 
			
		||||
		{
 | 
			
		||||
			seteuid(uid);
 | 
			
		||||
			setuid(uid);
 | 
			
		||||
		}
 | 
			
		||||
		if ( foreground )
 | 
			
		||||
		{
 | 
			
		||||
			setpgid(0, 0);
 | 
			
		||||
			sigprocmask(SIG_BLOCK, &sigttou, &oldset);
 | 
			
		||||
			tcsetpgrp(0, getpgid(0));
 | 
			
		||||
			sigprocmask(SIG_SETMASK, &oldset, NULL);
 | 
			
		||||
		}
 | 
			
		||||
		if ( quiet )
 | 
			
		||||
		{
 | 
			
		||||
			close(1);
 | 
			
		||||
			open("/dev/null", O_WRONLY);
 | 
			
		||||
		}
 | 
			
		||||
		if ( quiet_stderr )
 | 
			
		||||
		{
 | 
			
		||||
			close(2);
 | 
			
		||||
			open("/dev/null", O_WRONLY);
 | 
			
		||||
		}
 | 
			
		||||
		execvp(argv[0], (char* const*) argv);
 | 
			
		||||
		warn("%s", argv[0]);
 | 
			
		||||
		_exit(127);
 | 
			
		||||
	}
 | 
			
		||||
	int code;
 | 
			
		||||
	waitpid(child_pid, &code, 0);
 | 
			
		||||
	if ( foreground )
 | 
			
		||||
	{
 | 
			
		||||
		sigprocmask(SIG_BLOCK, &sigttou, &oldset);
 | 
			
		||||
		tcsetpgrp(0, getpgid(0));
 | 
			
		||||
		sigprocmask(SIG_SETMASK, &oldset, NULL);
 | 
			
		||||
	}
 | 
			
		||||
	if ( exit_on_failure )
 | 
			
		||||
	{
 | 
			
		||||
		if ( !WIFEXITED(code) || WEXITSTATUS(code) != 0 )
 | 
			
		||||
			(_exit_instead ? _exit : exit)(2);
 | 
			
		||||
	}
 | 
			
		||||
	int exit_status;
 | 
			
		||||
	if ( WIFEXITED(code) )
 | 
			
		||||
		exit_status = WEXITSTATUS(code);
 | 
			
		||||
	else
 | 
			
		||||
		exit_status = 128 + WTERMSIG(code);
 | 
			
		||||
	return raw_exit_code ? code : exit_status;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								sysinstall/execute.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								sysinstall/execute.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    execute.h
 | 
			
		||||
    Template for common execvp use cases.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef EXECUTE_H
 | 
			
		||||
#define EXECUTE_H
 | 
			
		||||
 | 
			
		||||
int execute(const char* const* argv, const char* flags, ...);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										85
									
								
								sysinstall/fileops.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								sysinstall/fileops.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,85 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    fileops.c
 | 
			
		||||
    File operation utility functions.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
#include <err.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "fileops.h"
 | 
			
		||||
 | 
			
		||||
int mkdir_p(const char* path, mode_t mode)
 | 
			
		||||
{
 | 
			
		||||
	int saved_errno = errno;
 | 
			
		||||
	if ( !mkdir(path, mode) )
 | 
			
		||||
		return 0;
 | 
			
		||||
	if ( errno == ENOENT )
 | 
			
		||||
	{
 | 
			
		||||
		char* prev = strdup(path);
 | 
			
		||||
		if ( !prev )
 | 
			
		||||
			return -1;
 | 
			
		||||
		int status =  mkdir_p(dirname(prev), mode | 0500);
 | 
			
		||||
		free(prev);
 | 
			
		||||
		if ( status < 0 )
 | 
			
		||||
			return -1;
 | 
			
		||||
		errno = saved_errno;
 | 
			
		||||
		if ( !mkdir(path, mode) )
 | 
			
		||||
			return 0;
 | 
			
		||||
	}
 | 
			
		||||
	if ( errno == EEXIST )
 | 
			
		||||
		return errno = saved_errno, 0;
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int access_or_die(const char* path, int mode)
 | 
			
		||||
{
 | 
			
		||||
	if ( access(path, mode) < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		if ( errno == EACCES ||
 | 
			
		||||
		     errno == ENOENT ||
 | 
			
		||||
             errno == ELOOP ||
 | 
			
		||||
             errno == ENAMETOOLONG ||
 | 
			
		||||
             errno == ENOTDIR )
 | 
			
		||||
			return -1;
 | 
			
		||||
		warn("%s", path);
 | 
			
		||||
		_exit(2);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mkdir_or_chmod_or_die(const char* path, mode_t mode)
 | 
			
		||||
{
 | 
			
		||||
	if ( mkdir(path, mode) == 0 )
 | 
			
		||||
		return;
 | 
			
		||||
	if ( errno == EEXIST )
 | 
			
		||||
	{
 | 
			
		||||
		if ( chmod(path, mode) == 0 )
 | 
			
		||||
			return;
 | 
			
		||||
		err(2, "chmod: %s", path);
 | 
			
		||||
	}
 | 
			
		||||
	err(2, "mkdir: %s", path);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								sysinstall/fileops.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								sysinstall/fileops.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    fileops.h
 | 
			
		||||
    File operation utility functions.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef FILEOPS_H
 | 
			
		||||
#define FILEOPS_H
 | 
			
		||||
 | 
			
		||||
int mkdir_p(const char* path, mode_t mode);
 | 
			
		||||
int access_or_die(const char* path, int mode);
 | 
			
		||||
void mkdir_or_chmod_or_die(const char* path, mode_t mode);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										222
									
								
								sysinstall/interactive.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								sysinstall/interactive.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,222 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    interactive.c
 | 
			
		||||
    Interactive utility functions.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <sys/termmode.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <err.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <termios.h>
 | 
			
		||||
#include <wchar.h>
 | 
			
		||||
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "interactive.h"
 | 
			
		||||
 | 
			
		||||
void shlvl(void)
 | 
			
		||||
{
 | 
			
		||||
	long shlvl = 0;
 | 
			
		||||
	if ( getenv("SHLVL") && (shlvl = atol(getenv("SHLVL"))) < 0 )
 | 
			
		||||
		shlvl = 0;
 | 
			
		||||
	if ( shlvl < LONG_MAX )
 | 
			
		||||
		shlvl++;
 | 
			
		||||
	char shlvl_string[sizeof(long) * 3];
 | 
			
		||||
	snprintf(shlvl_string, sizeof(shlvl_string), "%li", shlvl);
 | 
			
		||||
	setenv("SHLVL", shlvl_string, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void text(const char* str)
 | 
			
		||||
{
 | 
			
		||||
	fflush(stdout);
 | 
			
		||||
	struct winsize ws;
 | 
			
		||||
	if ( tcgetwinsize(1, &ws) < 0 )
 | 
			
		||||
		err(2, "tcgetwinsize");
 | 
			
		||||
	struct wincurpos wcp;
 | 
			
		||||
	if ( tcgetwincurpos(1, &wcp) < 0 )
 | 
			
		||||
		err(2, "tcgetwinsize");
 | 
			
		||||
	size_t columns = ws.ws_col;
 | 
			
		||||
	size_t column = wcp.wcp_col;
 | 
			
		||||
	bool blank = false;
 | 
			
		||||
	while ( str[0] )
 | 
			
		||||
	{
 | 
			
		||||
		if ( str[0] == '\n' )
 | 
			
		||||
		{
 | 
			
		||||
			putchar('\n');
 | 
			
		||||
			blank = false;
 | 
			
		||||
			column = 0;
 | 
			
		||||
			str++;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		else if ( isblank((unsigned char) str[0]) )
 | 
			
		||||
		{
 | 
			
		||||
			blank = true;
 | 
			
		||||
			str++;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		size_t word_length = 0;
 | 
			
		||||
		size_t word_columns = 0;
 | 
			
		||||
		mbstate_t ps = { 0 };
 | 
			
		||||
		while ( str[word_length] &&
 | 
			
		||||
		        str[word_length] != '\n' &&
 | 
			
		||||
		        !isblank((unsigned char) str[word_length]) )
 | 
			
		||||
		{
 | 
			
		||||
			wchar_t wc;
 | 
			
		||||
			size_t amount = mbrtowc(&wc, str + word_length, SIZE_MAX, &ps);
 | 
			
		||||
			if ( amount == (size_t) -2 )
 | 
			
		||||
				 break;
 | 
			
		||||
			if ( amount == (size_t) -1 )
 | 
			
		||||
			{
 | 
			
		||||
				memset(&ps, 0, sizeof(ps));
 | 
			
		||||
				amount = 1;
 | 
			
		||||
			}
 | 
			
		||||
			if ( amount == (size_t) 0 )
 | 
			
		||||
				break;
 | 
			
		||||
			word_length += amount;
 | 
			
		||||
			int width = wcwidth(wc);
 | 
			
		||||
			if ( width < 0 )
 | 
			
		||||
				continue;
 | 
			
		||||
			word_columns += width;
 | 
			
		||||
		}
 | 
			
		||||
		if ( (column && blank ? 1 : 0) + word_columns <= columns - column )
 | 
			
		||||
		{
 | 
			
		||||
			if ( column && blank )
 | 
			
		||||
			{
 | 
			
		||||
				putchar(' ');
 | 
			
		||||
				column++;
 | 
			
		||||
			}
 | 
			
		||||
			blank = false;
 | 
			
		||||
			fwrite(str, 1, word_length, stdout);
 | 
			
		||||
			column += word_columns;
 | 
			
		||||
			if ( column == columns )
 | 
			
		||||
				column = 0;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			if ( column != 0 && column != columns )
 | 
			
		||||
				putchar('\n');
 | 
			
		||||
			column = 0;
 | 
			
		||||
			blank = false;
 | 
			
		||||
			fwrite(str, 1, word_length, stdout);
 | 
			
		||||
			column += word_columns;
 | 
			
		||||
			column %= columns;
 | 
			
		||||
		}
 | 
			
		||||
		str += word_length;
 | 
			
		||||
	}
 | 
			
		||||
	fflush(stdout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void textf(const char* format, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	va_start(ap, format);
 | 
			
		||||
	char* str;
 | 
			
		||||
	int len = vasprintf(&str, format, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
	if ( len < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		vprintf(format, ap);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	text(str);
 | 
			
		||||
	free(str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void prompt(char* buffer,
 | 
			
		||||
            size_t buffer_size,
 | 
			
		||||
            const char* question,
 | 
			
		||||
            const char* answer)
 | 
			
		||||
{
 | 
			
		||||
	while ( true )
 | 
			
		||||
	{
 | 
			
		||||
		text(question);
 | 
			
		||||
		if ( answer )
 | 
			
		||||
			printf(" [%s] ", answer);
 | 
			
		||||
		else
 | 
			
		||||
			printf(" ");
 | 
			
		||||
		fflush(stdout);
 | 
			
		||||
		fgets(buffer, buffer_size, stdin);
 | 
			
		||||
		size_t buffer_length = strlen(buffer);
 | 
			
		||||
		if ( buffer_length && buffer[buffer_length-1] == '\n' )
 | 
			
		||||
			buffer[--buffer_length] = '\0';
 | 
			
		||||
		while ( buffer_length && buffer[buffer_length-1] == ' ' )
 | 
			
		||||
			buffer[--buffer_length] = '\0';
 | 
			
		||||
		if ( !strcmp(buffer, "") )
 | 
			
		||||
		{
 | 
			
		||||
			if ( !answer )
 | 
			
		||||
				continue;
 | 
			
		||||
			strlcpy(buffer, answer, buffer_size);
 | 
			
		||||
		}
 | 
			
		||||
		if ( !strcmp(buffer, "!") )
 | 
			
		||||
		{
 | 
			
		||||
			printf("Type 'exit' to return to install.\n");
 | 
			
		||||
			fflush(stdout);
 | 
			
		||||
			execute((const char*[]) { "sh", NULL }, "f");
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if ( !strcmp(buffer, "!man") )
 | 
			
		||||
		{
 | 
			
		||||
			execute((const char*[]) { "man", prompt_man_section,
 | 
			
		||||
			                          prompt_man_page, NULL }, "f");
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void password(char* buffer,
 | 
			
		||||
              size_t buffer_size,
 | 
			
		||||
              const char* question)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int termmode;
 | 
			
		||||
	gettermmode(0, &termmode);
 | 
			
		||||
	settermmode(0, termmode & ~TERMMODE_ECHO);
 | 
			
		||||
	text(question);
 | 
			
		||||
	printf(" ");
 | 
			
		||||
	fflush(stdout);
 | 
			
		||||
	fflush(stdin);
 | 
			
		||||
	// TODO: This may leave a copy of the password in the stdio buffer.
 | 
			
		||||
	fgets(buffer, buffer_size, stdin);
 | 
			
		||||
	fflush(stdin);
 | 
			
		||||
	printf("\n");
 | 
			
		||||
	size_t buffer_length = strlen(buffer);
 | 
			
		||||
	if ( buffer_length && buffer[buffer_length-1] == '\n' )
 | 
			
		||||
		buffer[--buffer_length] = '\0';
 | 
			
		||||
	settermmode(0, termmode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool has_program(const char* program)
 | 
			
		||||
{
 | 
			
		||||
	return execute((const char*[]) { "which", "--", program, NULL }, "q") == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool missing_program(const char* program)
 | 
			
		||||
{
 | 
			
		||||
	if ( has_program(program) )
 | 
			
		||||
		return false;
 | 
			
		||||
	warnx("%s: Program is absent", program);
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								sysinstall/interactive.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								sysinstall/interactive.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    interactive.h
 | 
			
		||||
    Interactive utility functions.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef INTERACTIVE_H
 | 
			
		||||
#define INTERACTIVE_H
 | 
			
		||||
 | 
			
		||||
extern const char* prompt_man_section;
 | 
			
		||||
extern const char* prompt_man_page;
 | 
			
		||||
 | 
			
		||||
void shlvl(void);
 | 
			
		||||
void text(const char* str);
 | 
			
		||||
__attribute__((format(printf, 1, 2)))
 | 
			
		||||
void textf(const char* format, ...);
 | 
			
		||||
void prompt(char* buffer,
 | 
			
		||||
            size_t buffer_size,
 | 
			
		||||
            const char* question,
 | 
			
		||||
            const char* answer);
 | 
			
		||||
void password(char* buffer,
 | 
			
		||||
              size_t buffer_size,
 | 
			
		||||
              const char* question);
 | 
			
		||||
bool missing_program(const char* program);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										411
									
								
								sysinstall/manifest.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										411
									
								
								sysinstall/manifest.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,411 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    manifest.c
 | 
			
		||||
    Manifest handling functions.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
#include <err.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <ioleast.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "fileops.h"
 | 
			
		||||
#include "manifest.h"
 | 
			
		||||
 | 
			
		||||
bool has_manifest(const char* manifest)
 | 
			
		||||
{
 | 
			
		||||
	char* path;
 | 
			
		||||
	if ( asprintf(&path, "/tix/manifest/%s", manifest) < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		warn("asprintf");
 | 
			
		||||
		_exit(2);
 | 
			
		||||
	}
 | 
			
		||||
	bool result = access(path, F_OK) == 0;
 | 
			
		||||
	free(path);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct hardlink
 | 
			
		||||
{
 | 
			
		||||
	dev_t dev;
 | 
			
		||||
	ino_t ino;
 | 
			
		||||
	char* path;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void install_manifest(const char* manifest,
 | 
			
		||||
                      const char* from_prefix,
 | 
			
		||||
                      const char* to_prefix)
 | 
			
		||||
{
 | 
			
		||||
	printf(" - Installing %s...\n", manifest);
 | 
			
		||||
	struct hardlink* hardlinks = NULL;
 | 
			
		||||
	size_t hardlinks_used = 0;
 | 
			
		||||
	size_t hardlinks_length = 0;
 | 
			
		||||
	size_t buffer_size = 1 << 16;
 | 
			
		||||
	char* buffer = malloc(buffer_size);
 | 
			
		||||
	if ( !buffer )
 | 
			
		||||
	{
 | 
			
		||||
		warn("malloc");
 | 
			
		||||
		_exit(2);
 | 
			
		||||
	}
 | 
			
		||||
	mode_t old_umask = umask(0000);
 | 
			
		||||
	char* inmanifest;
 | 
			
		||||
	if ( asprintf(&inmanifest, "%s/tix/manifest/%s", from_prefix, manifest) < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		warn("asprintf");
 | 
			
		||||
		_exit(2);
 | 
			
		||||
	}
 | 
			
		||||
	char* outmanifest;
 | 
			
		||||
	if ( asprintf(&outmanifest, "%s/tix/manifest/%s", to_prefix, manifest) < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		warn("asprintf");
 | 
			
		||||
		_exit(2);
 | 
			
		||||
	}
 | 
			
		||||
	FILE* fpin = fopen(inmanifest, "r");
 | 
			
		||||
	if ( !fpin )
 | 
			
		||||
	{
 | 
			
		||||
		warn("%s", inmanifest);
 | 
			
		||||
		_exit(2);
 | 
			
		||||
	}
 | 
			
		||||
	FILE* fpout = fopen(outmanifest, "w");
 | 
			
		||||
	if ( !fpout )
 | 
			
		||||
	{
 | 
			
		||||
		warn("%s", outmanifest);
 | 
			
		||||
		_exit(2);
 | 
			
		||||
	}
 | 
			
		||||
	char* line = NULL;
 | 
			
		||||
	size_t line_size = 0;
 | 
			
		||||
	ssize_t line_length;
 | 
			
		||||
	while ( 0 <= (errno = 0, line_length = getline(&line, &line_size, fpin)) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( line_length && line[line_length-1] == '\n' )
 | 
			
		||||
			line[--line_length] = '\0';
 | 
			
		||||
		if ( fprintf(fpout, "%s\n", line) < 0 )
 | 
			
		||||
		{
 | 
			
		||||
			warn("write: %s", outmanifest);
 | 
			
		||||
			_exit(2);
 | 
			
		||||
		}
 | 
			
		||||
		if ( line[0] != '/' )
 | 
			
		||||
			continue;
 | 
			
		||||
		char* in_path;
 | 
			
		||||
		if ( asprintf(&in_path, "%s%s", from_prefix, line) < 0 )
 | 
			
		||||
		{
 | 
			
		||||
			warn("asprintf");
 | 
			
		||||
			_exit(2);
 | 
			
		||||
		}
 | 
			
		||||
		char* out_path = line;
 | 
			
		||||
		if ( asprintf(&out_path, "%s%s", to_prefix, line) < 0 )
 | 
			
		||||
		{
 | 
			
		||||
			warn("asprintf");
 | 
			
		||||
			_exit(2);
 | 
			
		||||
		}
 | 
			
		||||
		struct stat inst;
 | 
			
		||||
		if ( lstat(in_path, &inst) < 0 )
 | 
			
		||||
		{
 | 
			
		||||
			warn("%s", in_path);
 | 
			
		||||
			_exit(2);
 | 
			
		||||
		}
 | 
			
		||||
		struct hardlink* hardlink = NULL;
 | 
			
		||||
		if ( S_ISREG(inst.st_mode) && 2 <= inst.st_nlink )
 | 
			
		||||
		{
 | 
			
		||||
			for ( size_t i = 0; i < hardlinks_used; i++ )
 | 
			
		||||
			{
 | 
			
		||||
				if ( hardlinks[i].dev != inst.st_dev ||
 | 
			
		||||
				     hardlinks[i].ino != inst.st_ino )
 | 
			
		||||
					continue;
 | 
			
		||||
				hardlink = &hardlinks[i];
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if ( hardlink )
 | 
			
		||||
		{
 | 
			
		||||
			unlink(out_path);
 | 
			
		||||
			if ( link(hardlink->path, out_path) < 0 )
 | 
			
		||||
			{
 | 
			
		||||
				warn("link: %s -> %s", hardlink->path, out_path);
 | 
			
		||||
				_exit(2);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( S_ISDIR(inst.st_mode) )
 | 
			
		||||
		{
 | 
			
		||||
			if ( mkdir(out_path, inst.st_mode & 07777) < 0 && errno != EEXIST )
 | 
			
		||||
			{
 | 
			
		||||
				warn("mkdir: %s", out_path);
 | 
			
		||||
				_exit(2);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( S_ISREG(inst.st_mode) )
 | 
			
		||||
		{
 | 
			
		||||
 | 
			
		||||
			int in_fd = open(in_path, O_RDONLY);
 | 
			
		||||
			if ( in_fd < 0 )
 | 
			
		||||
			{
 | 
			
		||||
				warn("%s", in_path);
 | 
			
		||||
				_exit(2);
 | 
			
		||||
			}
 | 
			
		||||
			unlink(out_path);
 | 
			
		||||
			int out_fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC,
 | 
			
		||||
			                  inst.st_mode & 07777);
 | 
			
		||||
			if ( out_fd < 0 )
 | 
			
		||||
			{
 | 
			
		||||
				warn("%s", out_path);
 | 
			
		||||
				_exit(2);
 | 
			
		||||
			}
 | 
			
		||||
			while ( true )
 | 
			
		||||
			{
 | 
			
		||||
				ssize_t amount = read(in_fd, buffer, buffer_size);
 | 
			
		||||
				if ( amount < 0 )
 | 
			
		||||
				{
 | 
			
		||||
					warn("read: %s", in_path);
 | 
			
		||||
					_exit(2);
 | 
			
		||||
				}
 | 
			
		||||
				if ( amount == 0 )
 | 
			
		||||
					break;
 | 
			
		||||
				if ( writeall(out_fd, buffer, (size_t) amount) < (size_t) amount )
 | 
			
		||||
				{
 | 
			
		||||
					warn("write: %s", out_path);
 | 
			
		||||
					_exit(2);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			close(out_fd);
 | 
			
		||||
			close(in_fd);
 | 
			
		||||
			if ( 2 <= inst.st_nlink )
 | 
			
		||||
			{
 | 
			
		||||
				if ( hardlinks_used == hardlinks_length )
 | 
			
		||||
				{
 | 
			
		||||
					// TODO: Multiplication overflow.
 | 
			
		||||
					size_t new_length = hardlinks_length ? 2 * hardlinks_length : 16;
 | 
			
		||||
					struct hardlink* new_hardlinks = (struct hardlink*)
 | 
			
		||||
						reallocarray(hardlinks, new_length, sizeof(struct hardlink));
 | 
			
		||||
					if ( !new_hardlinks )
 | 
			
		||||
					{
 | 
			
		||||
						warn("malloc");
 | 
			
		||||
						_exit(2);
 | 
			
		||||
					}
 | 
			
		||||
					hardlinks = new_hardlinks;
 | 
			
		||||
					hardlinks_length = new_length;
 | 
			
		||||
				}
 | 
			
		||||
				hardlinks[hardlinks_used].ino = inst.st_ino;
 | 
			
		||||
				hardlinks[hardlinks_used].dev = inst.st_dev;
 | 
			
		||||
				if ( !(hardlinks[hardlinks_used].path = strdup(out_path)) )
 | 
			
		||||
				{
 | 
			
		||||
					warn("strdup");
 | 
			
		||||
					_exit(2);
 | 
			
		||||
				}
 | 
			
		||||
				hardlinks_used++;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( S_ISLNK(inst.st_mode) )
 | 
			
		||||
		{
 | 
			
		||||
			ssize_t amount = readlink(in_path, buffer, buffer_size - 1);
 | 
			
		||||
			if ( amount < 0 )
 | 
			
		||||
			{
 | 
			
		||||
				warn("readlink: %s", in_path);
 | 
			
		||||
				_exit(2);
 | 
			
		||||
			}
 | 
			
		||||
			buffer[amount] = '\0';
 | 
			
		||||
			unlink(out_path);
 | 
			
		||||
			if ( symlink(buffer, out_path) < 0 && errno != EEXIST )
 | 
			
		||||
			{
 | 
			
		||||
				warn("symlink: %s", out_path);
 | 
			
		||||
				_exit(2);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			warnx("%s: Don't know how to copy this object", in_path);
 | 
			
		||||
			_exit(2);
 | 
			
		||||
		}
 | 
			
		||||
		free(in_path);
 | 
			
		||||
		free(out_path);
 | 
			
		||||
	}
 | 
			
		||||
	free(line);
 | 
			
		||||
	if ( errno )
 | 
			
		||||
	{
 | 
			
		||||
		warn("%s", inmanifest);
 | 
			
		||||
		_exit(2);
 | 
			
		||||
	}
 | 
			
		||||
	fclose(fpin);
 | 
			
		||||
	if ( fclose(fpout) == EOF )
 | 
			
		||||
	{
 | 
			
		||||
		warn("close: %s", outmanifest);
 | 
			
		||||
		_exit(2);
 | 
			
		||||
	}
 | 
			
		||||
	free(inmanifest);
 | 
			
		||||
	free(outmanifest);
 | 
			
		||||
	umask(old_umask);
 | 
			
		||||
	free(buffer);
 | 
			
		||||
	for ( size_t i = 0; i < hardlinks_used; i++ )
 | 
			
		||||
		free(hardlinks[i].path);
 | 
			
		||||
	free(hardlinks);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool check_installed(const char* path, const char* package)
 | 
			
		||||
{
 | 
			
		||||
	FILE* fp = fopen(path, "r");
 | 
			
		||||
	if ( !fp )
 | 
			
		||||
	{
 | 
			
		||||
		if ( errno != ENOENT )
 | 
			
		||||
			warn("%s", path);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	char* line = NULL;
 | 
			
		||||
	size_t line_size = 0;
 | 
			
		||||
	ssize_t line_length;
 | 
			
		||||
	while ( 0 < (errno = 0, line_length = getline(&line, &line_size, fp)) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( line[line_length-1] == '\n' )
 | 
			
		||||
			line[--line_length] = '\0';
 | 
			
		||||
		if ( !strcmp(line, package) )
 | 
			
		||||
		{
 | 
			
		||||
			free(line);
 | 
			
		||||
			fclose(fp);
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if ( errno != 0 )
 | 
			
		||||
		warn("%s", path);
 | 
			
		||||
	free(line);
 | 
			
		||||
	fclose(fp);
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char* shell_single_quote(const char* string)
 | 
			
		||||
{
 | 
			
		||||
	char* result;
 | 
			
		||||
	size_t result_size;
 | 
			
		||||
	FILE* fp = open_memstream(&result, &result_size);
 | 
			
		||||
	if (!fp)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	fputc('\'', fp);
 | 
			
		||||
	for ( size_t i = 0; string[i]; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		if ( string[i] == '\'' )
 | 
			
		||||
			fputs("\'\\\'\'", fp);
 | 
			
		||||
		else
 | 
			
		||||
			fputc((unsigned char) string[i], fp);
 | 
			
		||||
	}
 | 
			
		||||
	fputc('\'', fp);
 | 
			
		||||
	fflush(fp);
 | 
			
		||||
	int waserr = ferror(fp);
 | 
			
		||||
	fclose(fp);
 | 
			
		||||
	if (waserr) {
 | 
			
		||||
		free(result);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char* sort_file_cmd(const char* file)
 | 
			
		||||
{
 | 
			
		||||
	char* file_esc = shell_single_quote(file);
 | 
			
		||||
	if ( !file_esc )
 | 
			
		||||
		return NULL;
 | 
			
		||||
	char* cmd;
 | 
			
		||||
	if ( asprintf(&cmd, "sort -- %s", file_esc) < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		free(file_esc);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	free(file_esc);
 | 
			
		||||
	return cmd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void install_ports(const char* from_prefix, const char* to_prefix)
 | 
			
		||||
{
 | 
			
		||||
	char* inst_in_path;
 | 
			
		||||
	char* inst_out_path;
 | 
			
		||||
	if ( asprintf(&inst_in_path, "%s/tix/installed.list", from_prefix) < 0 ||
 | 
			
		||||
	     asprintf(&inst_out_path, "%s/tix/installed.list", to_prefix) < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		warn("asprintf");
 | 
			
		||||
		_exit(2);
 | 
			
		||||
	}
 | 
			
		||||
	if ( access_or_die(inst_in_path, F_OK) < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		free(inst_in_path);
 | 
			
		||||
		free(inst_out_path);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	char* cmd = sort_file_cmd(inst_in_path);
 | 
			
		||||
	if ( !cmd )
 | 
			
		||||
	{
 | 
			
		||||
		warn("sort_file_cmd");
 | 
			
		||||
		_exit(2);
 | 
			
		||||
	}
 | 
			
		||||
	FILE* fp = popen(cmd, "r");
 | 
			
		||||
	if ( !fp )
 | 
			
		||||
	{
 | 
			
		||||
		warn("%s", cmd);
 | 
			
		||||
		_exit(2);
 | 
			
		||||
	}
 | 
			
		||||
	char* line = NULL;
 | 
			
		||||
	size_t line_size = 0;
 | 
			
		||||
	ssize_t line_length;
 | 
			
		||||
	while ( 0 < (errno = 0, line_length = getline(&line, &line_size, fp)) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( line[line_length-1] == '\n' )
 | 
			
		||||
			line[--line_length] = '\0';
 | 
			
		||||
		if ( !check_installed(inst_out_path, line) )
 | 
			
		||||
		{
 | 
			
		||||
			FILE* inst_out_fp = fopen(inst_out_path, "a");
 | 
			
		||||
			if ( !inst_out_fp ||
 | 
			
		||||
				 fprintf(inst_out_fp, "%s\n", line) < 0 ||
 | 
			
		||||
				 fflush(inst_out_fp) == EOF )
 | 
			
		||||
			{
 | 
			
		||||
				warn("%s", inst_out_path);
 | 
			
		||||
				pclose(fp);
 | 
			
		||||
				_exit(2);
 | 
			
		||||
			}
 | 
			
		||||
			fclose(inst_out_fp);
 | 
			
		||||
		}
 | 
			
		||||
		char* tixinfo_in;
 | 
			
		||||
		char* tixinfo_out;
 | 
			
		||||
		if ( asprintf(&tixinfo_in, "%s/tix/tixinfo/%s", from_prefix, line) < 0 ||
 | 
			
		||||
		     asprintf(&tixinfo_out, "%s/tix/tixinfo/%s", to_prefix, line) < 0 )
 | 
			
		||||
		{
 | 
			
		||||
			warn("asprintf");
 | 
			
		||||
			pclose(fp);
 | 
			
		||||
			_exit(2);
 | 
			
		||||
		}
 | 
			
		||||
		execute((const char*[]) { "cp", "--", tixinfo_in, tixinfo_out, NULL }, "_e");
 | 
			
		||||
		free(tixinfo_in);
 | 
			
		||||
		free(tixinfo_out);
 | 
			
		||||
		install_manifest(line, from_prefix, to_prefix);
 | 
			
		||||
	}
 | 
			
		||||
	free(line);
 | 
			
		||||
	if ( errno )
 | 
			
		||||
	{
 | 
			
		||||
		warn("%s", cmd);
 | 
			
		||||
		pclose(fp);
 | 
			
		||||
		_exit(2);
 | 
			
		||||
	}
 | 
			
		||||
	pclose(fp);
 | 
			
		||||
	free(cmd);
 | 
			
		||||
	free(inst_in_path);
 | 
			
		||||
	free(inst_out_path);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								sysinstall/manifest.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								sysinstall/manifest.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    manifest.h
 | 
			
		||||
    Manifest handling functions.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef MANIFEST_H
 | 
			
		||||
#define MANIFEST_H
 | 
			
		||||
 | 
			
		||||
bool has_manifest(const char* manifest);
 | 
			
		||||
void install_manifest(const char* manifest,
 | 
			
		||||
                      const char* from_prefix,
 | 
			
		||||
                      const char* to_prefix);
 | 
			
		||||
bool check_installed(const char* path, const char* package);
 | 
			
		||||
void install_ports(const char* from_prefix, const char* to_prefix);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										206
									
								
								sysinstall/release.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								sysinstall/release.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,206 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    release.c
 | 
			
		||||
    Utility functions to handle release information.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <err.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "release.h"
 | 
			
		||||
 | 
			
		||||
void release_free(struct release* release)
 | 
			
		||||
{
 | 
			
		||||
	free(release->pretty_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char* os_release_eval(const char* string)
 | 
			
		||||
{
 | 
			
		||||
	char* result;
 | 
			
		||||
	size_t result_size;
 | 
			
		||||
	FILE* fp = open_memstream(&result, &result_size);
 | 
			
		||||
	if ( !fp )
 | 
			
		||||
		return NULL;
 | 
			
		||||
	bool escaped = false;
 | 
			
		||||
	bool singly_quote = false;
 | 
			
		||||
	bool doubly_quote = false;
 | 
			
		||||
	while ( *string )
 | 
			
		||||
	{
 | 
			
		||||
		char c = *string++;
 | 
			
		||||
		if ( !escaped && !singly_quote && c == '\\' )
 | 
			
		||||
			escaped = true;
 | 
			
		||||
		else if ( !escaped && !doubly_quote && c == '\'' )
 | 
			
		||||
			singly_quote = !singly_quote;
 | 
			
		||||
		else if ( !escaped && !singly_quote && c == '"' )
 | 
			
		||||
			doubly_quote = !doubly_quote;
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			fputc((unsigned char) c, fp);
 | 
			
		||||
			escaped = false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if ( fclose(fp) == EOF )
 | 
			
		||||
	{
 | 
			
		||||
		free(result);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void parse_version(const char* string,
 | 
			
		||||
                          unsigned long* major_ptr,
 | 
			
		||||
                          unsigned long* minor_ptr)
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
	*major_ptr = strtoul(string, (char**) &string, 10);
 | 
			
		||||
	if ( *string == '.' )
 | 
			
		||||
		string++;
 | 
			
		||||
	*minor_ptr = strtoul(string, (char**) &string, 10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void parse_release(const char* string,
 | 
			
		||||
                          unsigned long* major_ptr,
 | 
			
		||||
                          unsigned long* minor_ptr,
 | 
			
		||||
                          bool* dev_ptr)
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
	*major_ptr = strtoul(string, (char**) &string, 10);
 | 
			
		||||
	if ( *string == '.' )
 | 
			
		||||
		string++;
 | 
			
		||||
	*minor_ptr = strtoul(string, (char**) &string, 10);
 | 
			
		||||
	*dev_ptr = *string != '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool os_release_load(struct release* release,
 | 
			
		||||
                     const char* path,
 | 
			
		||||
                     const char* errpath)
 | 
			
		||||
{
 | 
			
		||||
	memset(release, 0, sizeof(*release));
 | 
			
		||||
	FILE* fp = fopen(path, "r");
 | 
			
		||||
	if ( !fp )
 | 
			
		||||
	{
 | 
			
		||||
		if ( errno != ENOENT )
 | 
			
		||||
			warn("%s", errpath);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	bool failure = false;
 | 
			
		||||
	bool success = false;
 | 
			
		||||
	bool found_id = false;
 | 
			
		||||
	bool found_pretty_name = false;
 | 
			
		||||
	bool found_sortix_abi = false;
 | 
			
		||||
	bool found_version_id = false;
 | 
			
		||||
	char* line = NULL;
 | 
			
		||||
	size_t line_size = 0;
 | 
			
		||||
	ssize_t line_length;
 | 
			
		||||
	while ( 0 < (errno = 0, line_length = getline(&line, &line_size, fp)) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( line[line_length-1] == '\n' )
 | 
			
		||||
			line[--line_length] = '\0';
 | 
			
		||||
		if ( !strncmp(line, "ID=", strlen("ID=")) )
 | 
			
		||||
		{
 | 
			
		||||
			const char* param = line + strlen("ID=");
 | 
			
		||||
			found_id = true;
 | 
			
		||||
			char* value = os_release_eval(param);
 | 
			
		||||
			if ( value )
 | 
			
		||||
			{
 | 
			
		||||
				if ( strcmp(value, "sortix") != 0 )
 | 
			
		||||
				{
 | 
			
		||||
					warn("%s: ID does not specify a 'sortix' system", errpath);
 | 
			
		||||
					failure = true;
 | 
			
		||||
				}
 | 
			
		||||
				free(value);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				warn("malloc");
 | 
			
		||||
				failure = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strncmp(line, "PRETTY_NAME=", strlen("PRETTY_NAME=")) )
 | 
			
		||||
		{
 | 
			
		||||
			const char* param = line + strlen("PRETTY_NAME=");
 | 
			
		||||
			found_pretty_name = true;
 | 
			
		||||
			if ( !(release->pretty_name = os_release_eval(param)) )
 | 
			
		||||
			{
 | 
			
		||||
				warn("malloc");
 | 
			
		||||
				failure = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strncmp(line, "SORTIX_ABI=", strlen("SORTIX_ABI=")) )
 | 
			
		||||
		{
 | 
			
		||||
			const char* param = line + strlen("SORTIX_ABI=");
 | 
			
		||||
			found_sortix_abi = true;
 | 
			
		||||
			char* value = os_release_eval(param);
 | 
			
		||||
			if ( value )
 | 
			
		||||
			{
 | 
			
		||||
				parse_version(value, &release->abi_major, &release->abi_minor);
 | 
			
		||||
				free(value);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				warn("malloc");
 | 
			
		||||
				failure = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strncmp(line, "VERSION_ID=", strlen("VERSION_ID=")) )
 | 
			
		||||
		{
 | 
			
		||||
			const char* param = line + strlen("VERSION_ID=");
 | 
			
		||||
			found_version_id = true;
 | 
			
		||||
			char* value = os_release_eval(param);
 | 
			
		||||
			if ( value )
 | 
			
		||||
			{
 | 
			
		||||
				parse_release(value, &release->version_major,
 | 
			
		||||
				              &release->version_minor, &release->version_dev);
 | 
			
		||||
				free(value);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				warn("malloc");
 | 
			
		||||
				failure = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if ( errno )
 | 
			
		||||
		warn("%s", errpath);
 | 
			
		||||
	else if ( failure )
 | 
			
		||||
		;
 | 
			
		||||
	else if ( !found_id )
 | 
			
		||||
		warnx("%s: No ID", errpath);
 | 
			
		||||
	else if ( !found_pretty_name )
 | 
			
		||||
		warnx("%s: No PRETTY_NAME", errpath);
 | 
			
		||||
	else if ( !found_sortix_abi )
 | 
			
		||||
		warnx("%s: No SORTIX_ABI", errpath);
 | 
			
		||||
	else if ( !found_version_id )
 | 
			
		||||
		warnx("%s: No VERSION_ID", errpath);
 | 
			
		||||
	else
 | 
			
		||||
		success = true;
 | 
			
		||||
	free(line);
 | 
			
		||||
	fclose(fp);
 | 
			
		||||
	if ( failure || !success )
 | 
			
		||||
	{
 | 
			
		||||
		release_free(release);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								sysinstall/release.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								sysinstall/release.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    release.h
 | 
			
		||||
    Utility functions to handle release information.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef RELEASE_H
 | 
			
		||||
#define RELEASE_H
 | 
			
		||||
 | 
			
		||||
struct release
 | 
			
		||||
{
 | 
			
		||||
	char* pretty_name;
 | 
			
		||||
	unsigned long version_major;
 | 
			
		||||
	unsigned long version_minor;
 | 
			
		||||
	bool version_dev;
 | 
			
		||||
	unsigned long abi_major;
 | 
			
		||||
	unsigned long abi_minor;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void release_free(struct release* release);
 | 
			
		||||
bool os_release_load(struct release* release,
 | 
			
		||||
                     const char* path,
 | 
			
		||||
                     const char* errpath);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										23
									
								
								sysinstall/sysinstall.8
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								sysinstall/sysinstall.8
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
.Dd $Mdocdate: January 5 2016 $
 | 
			
		||||
.Dt SYSINSTALL 8
 | 
			
		||||
.Os
 | 
			
		||||
.Sh NAME
 | 
			
		||||
.Nm sysinstall
 | 
			
		||||
.Nd operating system installer
 | 
			
		||||
.Sh SYNOPSIS
 | 
			
		||||
.Nm sysinstall
 | 
			
		||||
.Sh DESCRIPTION
 | 
			
		||||
.Nm
 | 
			
		||||
is an interactive command line program that creates a new installation of the
 | 
			
		||||
operating system.  It asks basic questions, prepares a new root filesystem,
 | 
			
		||||
makes a copy of the current operating system there and configures it for usage.
 | 
			
		||||
The installation proceeds as described in
 | 
			
		||||
.Xr installation 7 .
 | 
			
		||||
.Pp
 | 
			
		||||
.Nm
 | 
			
		||||
must be run as root with stdin, stdout and stderr all pointing to the terminal.
 | 
			
		||||
It is intended to be run from a live environment and not from an actual
 | 
			
		||||
installation.
 | 
			
		||||
.Sh SEE ALSO
 | 
			
		||||
.Xr installation 7 ,
 | 
			
		||||
.Xr sysupgrade 8
 | 
			
		||||
							
								
								
									
										985
									
								
								sysinstall/sysinstall.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										985
									
								
								sysinstall/sysinstall.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,985 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    sysinstall.c
 | 
			
		||||
    Operating system installer.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <sys/display.h>
 | 
			
		||||
#include <sys/kernelinfo.h>
 | 
			
		||||
#include <sys/mount.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/utsname.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <brand.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <err.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fstab.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <pwd.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
// Sortix libc doesn't have its own proper <limits.h> at this time.
 | 
			
		||||
#if defined(__sortix__)
 | 
			
		||||
#include <sortix/limits.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <mount/blockdevice.h>
 | 
			
		||||
#include <mount/devices.h>
 | 
			
		||||
#include <mount/filesystem.h>
 | 
			
		||||
#include <mount/harddisk.h>
 | 
			
		||||
#include <mount/partition.h>
 | 
			
		||||
#include <mount/uuid.h>
 | 
			
		||||
 | 
			
		||||
#include "devices.h"
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "fileops.h"
 | 
			
		||||
#include "interactive.h"
 | 
			
		||||
#include "manifest.h"
 | 
			
		||||
 | 
			
		||||
const char* prompt_man_section = "7";
 | 
			
		||||
const char* prompt_man_page = "installation";
 | 
			
		||||
 | 
			
		||||
static struct partition_table* search_bios_boot_pt(struct filesystem* root_fs)
 | 
			
		||||
{
 | 
			
		||||
	char firmware[64];
 | 
			
		||||
	if ( kernelinfo("firmware", firmware, sizeof(firmware)) != 0 )
 | 
			
		||||
		return NULL;
 | 
			
		||||
	if ( strcmp(firmware, "bios") != 0 )
 | 
			
		||||
		return NULL;
 | 
			
		||||
	struct blockdevice* bdev = root_fs->bdev;
 | 
			
		||||
	while ( bdev->p )
 | 
			
		||||
		bdev = bdev->p->parent_bdev;
 | 
			
		||||
	if ( !bdev->pt )
 | 
			
		||||
		return NULL;
 | 
			
		||||
	struct partition_table* pt = bdev->pt;
 | 
			
		||||
	if ( pt->type != PARTITION_TABLE_TYPE_GPT )
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return pt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct partition* search_bios_boot_search(struct partition_table* pt)
 | 
			
		||||
{
 | 
			
		||||
	for ( size_t i = 0; i < pt->partitions_count; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		struct partition* p = pt->partitions[i];
 | 
			
		||||
		if ( p->bdev.fs && !strcmp(p->bdev.fs->fstype_name, "biosboot") )
 | 
			
		||||
			return p;
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct partition* search_bios_boot_partition(struct filesystem* root_fs)
 | 
			
		||||
{
 | 
			
		||||
	struct partition_table* pt = search_bios_boot_pt(root_fs);
 | 
			
		||||
	if ( !pt )
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return search_bios_boot_search(pt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool missing_bios_boot_partition(struct filesystem* root_fs)
 | 
			
		||||
{
 | 
			
		||||
	struct partition_table* pt = search_bios_boot_pt(root_fs);
 | 
			
		||||
	if ( !pt )
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return !search_bios_boot_search(pt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool passwd_check(const char* passwd_path,
 | 
			
		||||
                         bool (*check)(struct passwd*, void*),
 | 
			
		||||
                         void* check_ctx)
 | 
			
		||||
{
 | 
			
		||||
	FILE* passwd = fopen(passwd_path, "r");
 | 
			
		||||
	if ( !passwd )
 | 
			
		||||
	{
 | 
			
		||||
		if ( errno != ENOENT )
 | 
			
		||||
			warn("%s", passwd_path);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	struct passwd* pwd;
 | 
			
		||||
	while ( (errno = 0, pwd = fgetpwent(passwd)) )
 | 
			
		||||
	{
 | 
			
		||||
		if ( check(pwd, check_ctx) )
 | 
			
		||||
		{
 | 
			
		||||
			fclose(passwd);
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if ( errno != 0 )
 | 
			
		||||
		warn("%s", passwd_path);
 | 
			
		||||
	fclose(passwd);
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool passwd_has_uid_check(struct passwd* pwd, void* ctx)
 | 
			
		||||
{
 | 
			
		||||
	return pwd->pw_uid == *(uid_t*) ctx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool passwd_has_uid(const char* passwd_path, uid_t uid)
 | 
			
		||||
{
 | 
			
		||||
	return passwd_check(passwd_path, passwd_has_uid_check, &uid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool passwd_has_name_check(struct passwd* pwd, void* ctx)
 | 
			
		||||
{
 | 
			
		||||
	return !strcmp(pwd->pw_name, (const char*) ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool passwd_has_name(const char* passwd_path, const char* name)
 | 
			
		||||
{
 | 
			
		||||
	return passwd_check(passwd_path, passwd_has_name_check, (void*) name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void install_skel(const char* home, uid_t uid, gid_t gid)
 | 
			
		||||
{
 | 
			
		||||
	const char* argv[] = { "cp", "-RT", "--", "etc/skel", home, NULL };
 | 
			
		||||
	execute(argv, "ug", uid, gid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__attribute__((format(printf, 3, 4)))
 | 
			
		||||
static bool install_configurationf(const char* path,
 | 
			
		||||
                                   const char* mode,
 | 
			
		||||
                                   const char* format,
 | 
			
		||||
                                   ...)
 | 
			
		||||
{
 | 
			
		||||
	FILE* fp = fopen(path, mode);
 | 
			
		||||
	if ( !fp )
 | 
			
		||||
	{
 | 
			
		||||
		warn("%s", path);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	va_start(ap, format);
 | 
			
		||||
	int status = vfprintf(fp, format, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
	if ( status < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		warn("%s", path);
 | 
			
		||||
		fclose(fp);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	if ( fclose(fp) == EOF )
 | 
			
		||||
	{
 | 
			
		||||
		warn("%s", path);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void grub_hash_password(char* buffer, size_t buffer_size, const char* pw)
 | 
			
		||||
{
 | 
			
		||||
	int pipe_fds[2];
 | 
			
		||||
	if ( pipe(pipe_fds) < 0 )
 | 
			
		||||
		err(2, "pipe");
 | 
			
		||||
	pid_t pid = fork();
 | 
			
		||||
	if ( pid < 0 )
 | 
			
		||||
		err(2, "fork");
 | 
			
		||||
	if ( pid == 0 )
 | 
			
		||||
	{
 | 
			
		||||
		close(pipe_fds[0]);
 | 
			
		||||
		if ( dup2(pipe_fds[1], 1) < 0 )
 | 
			
		||||
			_exit(2);
 | 
			
		||||
		close(pipe_fds[1]);
 | 
			
		||||
		const char* argv[] = { "grub-mkpasswd-pbkdf2", "-p", pw, NULL };
 | 
			
		||||
		execvp(argv[0], (char* const*) argv);
 | 
			
		||||
		_exit(127);
 | 
			
		||||
	}
 | 
			
		||||
	close(pipe_fds[1]);
 | 
			
		||||
	size_t done = 0;
 | 
			
		||||
	while ( done < buffer_size )
 | 
			
		||||
	{
 | 
			
		||||
		ssize_t amount = read(pipe_fds[0], buffer + done, buffer_size - done);
 | 
			
		||||
		if ( amount < 0 )
 | 
			
		||||
			err(2, "read");
 | 
			
		||||
		if ( amount == 0 )
 | 
			
		||||
			break;
 | 
			
		||||
		done += amount;
 | 
			
		||||
	}
 | 
			
		||||
	if ( done && buffer[done-1] == '\n' )
 | 
			
		||||
		done--;
 | 
			
		||||
	if ( done == buffer_size )
 | 
			
		||||
		done--;
 | 
			
		||||
	buffer[done] = '\0';
 | 
			
		||||
	close(pipe_fds[0]);
 | 
			
		||||
	int exit_code;
 | 
			
		||||
	waitpid(pid, &exit_code, 0);
 | 
			
		||||
	if ( !WIFEXITED(exit_code) || WEXITSTATUS(exit_code) != 0 )
 | 
			
		||||
		errx(2, "grub password hash failed");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pid_t main_pid;
 | 
			
		||||
static struct mountpoint* mountpoints = NULL;
 | 
			
		||||
static size_t mountpoints_used = 0;
 | 
			
		||||
static bool etc_made = false;
 | 
			
		||||
static char etc[] = "/tmp/etc.XXXXXX";
 | 
			
		||||
static bool fs_made = false;
 | 
			
		||||
static char fs[] = "/tmp/fs.XXXXXX";
 | 
			
		||||
 | 
			
		||||
static void unmount_all_but_root()
 | 
			
		||||
{
 | 
			
		||||
	for ( size_t n = mountpoints_used; n != 0; n-- )
 | 
			
		||||
	{
 | 
			
		||||
		size_t i = n - 1;
 | 
			
		||||
		struct mountpoint* mountpoint = &mountpoints[i];
 | 
			
		||||
		if ( !strcmp(mountpoint->entry.fs_file, "/") )
 | 
			
		||||
			continue;
 | 
			
		||||
		mountpoint_unmount(mountpoint);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void exit_handler(void)
 | 
			
		||||
{
 | 
			
		||||
	if ( getpid() != main_pid )
 | 
			
		||||
		 return;
 | 
			
		||||
	chdir("/");
 | 
			
		||||
	for ( size_t n = mountpoints_used; n != 0; n-- )
 | 
			
		||||
	{
 | 
			
		||||
		size_t i = n - 1;
 | 
			
		||||
		struct mountpoint* mountpoint = &mountpoints[i];
 | 
			
		||||
		mountpoint_unmount(mountpoint);
 | 
			
		||||
	}
 | 
			
		||||
	if ( fs_made )
 | 
			
		||||
		rmdir(fs);
 | 
			
		||||
	if ( etc_made )
 | 
			
		||||
		execute((const char*[]) { "rm", "-rf", etc, NULL }, "");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(void)
 | 
			
		||||
{
 | 
			
		||||
	shlvl();
 | 
			
		||||
 | 
			
		||||
	if ( !isatty(0) )
 | 
			
		||||
		errx(2, "fatal: stdin is not a terminal");
 | 
			
		||||
	if ( !isatty(1) )
 | 
			
		||||
		errx(2, "fatal: stdout is not a terminal");
 | 
			
		||||
	if ( !isatty(2) )
 | 
			
		||||
		errx(2, "fatal: stderr is not a terminal");
 | 
			
		||||
 | 
			
		||||
	if ( getuid() != 0 )
 | 
			
		||||
		errx(2, "You need to be root to install %s", BRAND_DISTRIBUTION_NAME);
 | 
			
		||||
	if ( getgid() != 0 )
 | 
			
		||||
		errx(2, "You need to be group root to install %s", BRAND_DISTRIBUTION_NAME);
 | 
			
		||||
 | 
			
		||||
	main_pid = getpid();
 | 
			
		||||
	if ( atexit(exit_handler) != 0 )
 | 
			
		||||
		err(2, "atexit");
 | 
			
		||||
 | 
			
		||||
	if ( !mkdtemp(etc) )
 | 
			
		||||
		err(2, "mkdtemp: %s", "/tmp/etc.XXXXXX");
 | 
			
		||||
	etc_made = true;
 | 
			
		||||
 | 
			
		||||
	if ( chdir(etc) < 0 )
 | 
			
		||||
		err(2, "chdir: %s", etc);
 | 
			
		||||
 | 
			
		||||
	struct utsname uts;
 | 
			
		||||
	uname(&uts);
 | 
			
		||||
 | 
			
		||||
	static char input[256];
 | 
			
		||||
 | 
			
		||||
	textf("Hello and welcome to the " BRAND_DISTRIBUTION_NAME " " VERSIONSTR ""
 | 
			
		||||
	      " installer for %s.\n\n", uts.machine);
 | 
			
		||||
 | 
			
		||||
	// '|' rather than '||' is to ensure side effects.
 | 
			
		||||
	if ( missing_program("cut") |
 | 
			
		||||
	     missing_program("dash") |
 | 
			
		||||
	     missing_program("fsck.ext2") |
 | 
			
		||||
	     missing_program("grub-install") |
 | 
			
		||||
	     missing_program("man") |
 | 
			
		||||
	     missing_program("sed") |
 | 
			
		||||
	     missing_program("xargs") )
 | 
			
		||||
	{
 | 
			
		||||
		text("Warning: This system does not have the necessary third party "
 | 
			
		||||
		     "software installed to properly install this operating system.\n");
 | 
			
		||||
		while ( true )
 | 
			
		||||
		{
 | 
			
		||||
			prompt(input, sizeof(input), "Sure you want to proceed?", "no");
 | 
			
		||||
			if ( strcasecmp(input, "no") == 0 )
 | 
			
		||||
				return 0;
 | 
			
		||||
			if ( strcasecmp(input, "yes") == 0 )
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		text("\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	text("You are about to install a new operating system on this computer. "
 | 
			
		||||
	     "This is not something you should do on a whim or when you are "
 | 
			
		||||
         "impatient. Take the time to read the documentation and be patient "
 | 
			
		||||
	     "while you learn the new system. This is a very good time to start an "
 | 
			
		||||
		 "external music player that plays soothing classical music on loop.\n\n");
 | 
			
		||||
	const char* readies[] =
 | 
			
		||||
	{
 | 
			
		||||
		"Ready",
 | 
			
		||||
		"Yes",
 | 
			
		||||
		"Yeah",
 | 
			
		||||
		"Yep",
 | 
			
		||||
		"Let's go",
 | 
			
		||||
		"Let's do this",
 | 
			
		||||
		"Betcha",
 | 
			
		||||
		"Sure am",
 | 
			
		||||
		"You bet",
 | 
			
		||||
		"It's very good music",
 | 
			
		||||
	};
 | 
			
		||||
	size_t num_readies = sizeof(readies) / sizeof(readies[0]);
 | 
			
		||||
	const char* ready = readies[arc4random_uniform(num_readies)];
 | 
			
		||||
	prompt(input, sizeof(input), "Ready?", ready);
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	text("This is not yet a fully fledged operating system. You should adjust "
 | 
			
		||||
	     "your expectations accordingly. You should not consider the system safe "
 | 
			
		||||
	     "for multi-user use. Filesystem permissions are not enforced yet. There "
 | 
			
		||||
	     "are many security issues so setuid(2) blatantly allows every user to "
 | 
			
		||||
	     "become root if they want to.\n\n");
 | 
			
		||||
 | 
			
		||||
	text("You can always escape to a shell by answering '!' to any regular "
 | 
			
		||||
	     "prompt. You can view the installation(7) manual page by "
 | 
			
		||||
	     "answering '!man'. Default answers are in []'s and can be selected by "
 | 
			
		||||
	     "pressing enter.\n\n");
 | 
			
		||||
	// TODO: You can leave this program by pressing ^C but it can leave your
 | 
			
		||||
	//       system in an inconsistent state.
 | 
			
		||||
 | 
			
		||||
	install_configurationf("upgrade.conf", "a", "src = yes\n");
 | 
			
		||||
 | 
			
		||||
	while ( true )
 | 
			
		||||
	{
 | 
			
		||||
		// TODO: Detect the name of the current keyboard layout.
 | 
			
		||||
		prompt(input, sizeof(input),
 | 
			
		||||
		       "Choose your keyboard layout ('?' or 'L' for list)", "default");
 | 
			
		||||
		if ( !strcmp(input, "?") ||
 | 
			
		||||
		     !strcmp(input, "l") ||
 | 
			
		||||
		     !strcmp(input, "L") )
 | 
			
		||||
		{
 | 
			
		||||
			DIR* dir = opendir("/share/kblayout");
 | 
			
		||||
			if ( !dir )
 | 
			
		||||
			{
 | 
			
		||||
				warn("%s", "/share/kblayout");
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			bool space = false;
 | 
			
		||||
			struct dirent* entry;
 | 
			
		||||
			while ( (entry = readdir(dir)) )
 | 
			
		||||
			{
 | 
			
		||||
				if ( entry->d_name[0] == '.' )
 | 
			
		||||
					continue;
 | 
			
		||||
				if ( space )
 | 
			
		||||
					putchar(' ');
 | 
			
		||||
				fputs(entry->d_name, stdout);
 | 
			
		||||
				space = true;
 | 
			
		||||
			}
 | 
			
		||||
			closedir(dir);
 | 
			
		||||
			if ( !space )
 | 
			
		||||
				fputs("(No keyboard layouts available)", stdout);
 | 
			
		||||
			putchar('\n');
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if ( !strcmp(input, "default") )
 | 
			
		||||
			break;
 | 
			
		||||
		const char* argv[] = { "chkblayout", "--", input, NULL };
 | 
			
		||||
		if ( execute(argv, "f") == 0 )
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	if ( !input[0] || !strcmp(input, "default") )
 | 
			
		||||
		text("/etc/kblayout will not be created (default).\n");
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		textf("/etc/kblayout will be set to \"%s\".\n", input);
 | 
			
		||||
		mode_t old_umask = getumask();
 | 
			
		||||
		umask(022);
 | 
			
		||||
		install_configurationf("kblayout", "w", "%s\n", input);
 | 
			
		||||
		umask(old_umask);
 | 
			
		||||
	}
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	struct dispmsg_get_driver_name dgdn = { 0 };
 | 
			
		||||
	dgdn.msgid = DISPMSG_GET_DRIVER_NAME;
 | 
			
		||||
	dgdn.device = 0;
 | 
			
		||||
	dgdn.driver_index = 0;
 | 
			
		||||
	dgdn.name.byte_size = 0;
 | 
			
		||||
	dgdn.name.str = NULL;
 | 
			
		||||
	if ( dispmsg_issue(&dgdn, sizeof(dgdn)) == 0 || errno != ENODEV )
 | 
			
		||||
	{
 | 
			
		||||
		while ( true )
 | 
			
		||||
		{
 | 
			
		||||
			prompt(input, sizeof(input),
 | 
			
		||||
			       "Select default display resolution? (yes/no)", "yes");
 | 
			
		||||
			if ( strcasecmp(input, "no") && strcasecmp(input, "yes") )
 | 
			
		||||
				continue;
 | 
			
		||||
			bool was_no = strcasecmp(input, "no") == 0;
 | 
			
		||||
			input[0] = '\0';
 | 
			
		||||
			if ( was_no )
 | 
			
		||||
				break;
 | 
			
		||||
			if ( execute((const char*[]) { "chvideomode", NULL }, "f") != 0 )
 | 
			
		||||
				continue;
 | 
			
		||||
			struct dispmsg_get_crtc_mode get_mode;
 | 
			
		||||
			get_mode.msgid = DISPMSG_GET_CRTC_MODE;
 | 
			
		||||
			get_mode.device = 0;
 | 
			
		||||
			get_mode.connector = 0;
 | 
			
		||||
			if ( dispmsg_issue(&get_mode, sizeof(get_mode)) < 0 )
 | 
			
		||||
				break;
 | 
			
		||||
			if ( !(get_mode.mode.control & DISPMSG_CONTROL_VALID) )
 | 
			
		||||
				break;
 | 
			
		||||
			if ( get_mode.mode.control & DISPMSG_CONTROL_VGA )
 | 
			
		||||
				break;
 | 
			
		||||
			snprintf(input, sizeof(input), "%ux%ux%u",
 | 
			
		||||
			         get_mode.mode.view_xres,
 | 
			
		||||
			         get_mode.mode.view_yres,
 | 
			
		||||
			         get_mode.mode.fb_format);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if ( !input[0] )
 | 
			
		||||
		text("/etc/videomode will not be created.\n");
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		textf("/etc/videomode will be set to \"%s\".\n", input);
 | 
			
		||||
		mode_t old_umask = getumask();
 | 
			
		||||
		umask(022);
 | 
			
		||||
		install_configurationf("videomode", "w", "%s\n", input);
 | 
			
		||||
		umask(old_umask);
 | 
			
		||||
	}
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	scan_devices();
 | 
			
		||||
 | 
			
		||||
	textf("You need a bootloader to start the operating system. GRUB is the "
 | 
			
		||||
	      "standard %s bootloader and this installer comes with a copy. "
 | 
			
		||||
	      "This copy is however unable to automatically detect other operating "
 | 
			
		||||
	      "systems.\n\n", BRAND_DISTRIBUTION_NAME);
 | 
			
		||||
	text("Single-boot installations should accept this bootloader.\n");
 | 
			
		||||
	text("Dual-boot systems should refuse it and manually arrange for "
 | 
			
		||||
	     "bootloading by configuring any existing multiboot compliant "
 | 
			
		||||
	     "bootloader.\n");
 | 
			
		||||
	text("\n");
 | 
			
		||||
	char accept_grub[10];
 | 
			
		||||
	char accept_grub_password[10];
 | 
			
		||||
	char grub_password[512];
 | 
			
		||||
	while ( true )
 | 
			
		||||
	{
 | 
			
		||||
		const char* def = "yes";
 | 
			
		||||
		if ( check_existing_systems() )
 | 
			
		||||
			def = "no";
 | 
			
		||||
		prompt(accept_grub, sizeof(accept_grub),
 | 
			
		||||
		       "Install a new GRUB bootloader?", def);
 | 
			
		||||
		if ( strcasecmp(accept_grub, "no") == 0 ||
 | 
			
		||||
		     strcasecmp(accept_grub, "yes") == 0 )
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	if ( strcasecmp(accept_grub, "yes") == 0 )
 | 
			
		||||
	{
 | 
			
		||||
		install_configurationf("upgrade.conf", "a", "grub = yes\n");
 | 
			
		||||
 | 
			
		||||
		text("If an unauthorized person has access to the bootloader command "
 | 
			
		||||
		     "line, then the whole system security can be compromised. You can "
 | 
			
		||||
		     "prevent this by password protecting interactive use of the "
 | 
			
		||||
		     "bootloader, but still allowing anyone to start the system "
 | 
			
		||||
		     "normally. Similarly you may wish to manually go into your "
 | 
			
		||||
		     "firmware and password protect it.\n");
 | 
			
		||||
		text("\n");
 | 
			
		||||
		while ( true )
 | 
			
		||||
		{
 | 
			
		||||
			prompt(accept_grub_password, sizeof(accept_grub_password),
 | 
			
		||||
			       "Password protect interactive bootloader? (yes/no)", "yes");
 | 
			
		||||
			if ( strcasecmp(accept_grub_password, "no") == 0 ||
 | 
			
		||||
			     strcasecmp(accept_grub_password, "yes") == 0 )
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		while ( !strcasecmp(accept_grub_password, "yes") )
 | 
			
		||||
		{
 | 
			
		||||
			char first[128];
 | 
			
		||||
			char second[128];
 | 
			
		||||
			password(first, sizeof(first),
 | 
			
		||||
			         "Bootloader root password? (will not echo)");
 | 
			
		||||
			password(second, sizeof(second),
 | 
			
		||||
			         "Bootloader root password? (again)");
 | 
			
		||||
			if ( strcmp(first, second) != 0 )
 | 
			
		||||
			{
 | 
			
		||||
				printf("Passwords do not match, try again.\n");
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			explicit_bzero(second, sizeof(second));
 | 
			
		||||
			if ( !strcmp(first, "") )
 | 
			
		||||
			{
 | 
			
		||||
				char answer[32];
 | 
			
		||||
				prompt(answer, sizeof(answer),
 | 
			
		||||
					   "Empty password is stupid, are you sure? (yes/no)", "no");
 | 
			
		||||
				if ( strcasecmp(answer, "yes") != 0 )
 | 
			
		||||
					continue;
 | 
			
		||||
			}
 | 
			
		||||
			grub_hash_password(grub_password, sizeof(grub_password), first);
 | 
			
		||||
			textf("/etc/grubpw will be made with grub-mkpasswd-pbkdf2.\n");
 | 
			
		||||
			mode_t old_umask = getumask();
 | 
			
		||||
			umask(077);
 | 
			
		||||
			install_configurationf("grubpw", "w", "%s\n", grub_password);
 | 
			
		||||
			umask(old_umask);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		text("\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO: Offer the user an automatic layout of partitions if the disk is
 | 
			
		||||
	//       empty.
 | 
			
		||||
 | 
			
		||||
	// TODO: Perhaps let the user know the size of the system that will be
 | 
			
		||||
	//       installed?
 | 
			
		||||
 | 
			
		||||
	text("You need select a root filesystem and other mountpoints now. You "
 | 
			
		||||
	     "will now be dumped into a partition editor. Create and format a "
 | 
			
		||||
	     "root filesystem partition as needed.\n");
 | 
			
		||||
	text("\n");
 | 
			
		||||
	const char* mktable_tip = "";
 | 
			
		||||
	if ( check_lacking_partition_table() )
 | 
			
		||||
		mktable_tip = "Type mktable to make a new partition table. ";
 | 
			
		||||
	textf("Type ls to list partitions on the device. "
 | 
			
		||||
	      "%s"
 | 
			
		||||
	      "Type mkpart to make a new partition. "
 | 
			
		||||
	      "Type mount 2 /mnt to create a mountpoint for partition 2. "
 | 
			
		||||
	      "Type exit when done. "
 | 
			
		||||
	      "There is partitioning advise in installation(7). "
 | 
			
		||||
	      "Type man 8 disked to display the disked(8) man page.\n",
 | 
			
		||||
	      mktable_tip);
 | 
			
		||||
	struct filesystem* root_filesystem = NULL;
 | 
			
		||||
	bool not_first = false;
 | 
			
		||||
	while ( true )
 | 
			
		||||
	{
 | 
			
		||||
		if ( not_first )
 | 
			
		||||
			text("Type man to display the disked(8) man page.\n");
 | 
			
		||||
		not_first = true;
 | 
			
		||||
		const char* argv[] = { "disked", "--fstab=fstab", NULL };
 | 
			
		||||
		if ( execute(argv, "f") != 0 )
 | 
			
		||||
		{
 | 
			
		||||
			// TODO: We also end up here on SIGINT.
 | 
			
		||||
			// TODO: Offer a shell here instead of failing?
 | 
			
		||||
			warnx("partitioning failed");
 | 
			
		||||
			sleep(1);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		free_mountpoints(mountpoints, mountpoints_used);
 | 
			
		||||
		mountpoints = NULL;
 | 
			
		||||
		mountpoints_used = 0;
 | 
			
		||||
		scan_devices();
 | 
			
		||||
		if ( !load_mountpoints("fstab", &mountpoints, &mountpoints_used) )
 | 
			
		||||
		{
 | 
			
		||||
			if ( errno == ENOENT )
 | 
			
		||||
				text("You have not created any mountpoints. Try again.\n");
 | 
			
		||||
			else
 | 
			
		||||
				warn("fstab");
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		bool found_rootfs = false;
 | 
			
		||||
		for ( size_t i = 0; !found_rootfs && i < mountpoints_used; i++ )
 | 
			
		||||
			if ( !strcmp(mountpoints[i].entry.fs_file, "/") )
 | 
			
		||||
				found_rootfs = true;
 | 
			
		||||
		if ( !found_rootfs )
 | 
			
		||||
		{
 | 
			
		||||
			text("You have no root filesystem mountpoint. Try again.\n");
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		root_filesystem = NULL;
 | 
			
		||||
		for ( size_t i = 0; i < mountpoints_used; i++ )
 | 
			
		||||
		{
 | 
			
		||||
			struct mountpoint* mnt = &mountpoints[i];
 | 
			
		||||
			const char* spec = mnt->entry.fs_spec;
 | 
			
		||||
			if ( !(mnt->fs = search_for_filesystem_by_spec(spec)) )
 | 
			
		||||
			{
 | 
			
		||||
				warnx("fstab: Found no filesystem matching `%s'", spec);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if ( !mnt->fs->driver )
 | 
			
		||||
			{
 | 
			
		||||
				textf("Don't know how to mount a root filesystem of type %s. "
 | 
			
		||||
					  "Try again.\n", mnt->fs->fstype_name);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if ( !strcmp(mnt->entry.fs_file, "/") )
 | 
			
		||||
				root_filesystem = mnt->fs;
 | 
			
		||||
		}
 | 
			
		||||
		assert(root_filesystem);
 | 
			
		||||
		if ( !strcasecmp(accept_grub, "yes") &&
 | 
			
		||||
		     missing_bios_boot_partition(root_filesystem) )
 | 
			
		||||
		{
 | 
			
		||||
			textf("You are a installing BIOS bootloader and the root "
 | 
			
		||||
			      "filesystem is located on a GPT partition, but you haven't "
 | 
			
		||||
			      "made a BIOS boot partition on the root GPT disk. Pick "
 | 
			
		||||
			      "biosboot during mkpart and make a 1 MiB partition.\n");
 | 
			
		||||
			char return_to_disked[10];
 | 
			
		||||
			while ( true )
 | 
			
		||||
			{
 | 
			
		||||
				prompt(return_to_disked, sizeof(return_to_disked),
 | 
			
		||||
					   "Return to disked to make a BIOS boot partition?", "yes");
 | 
			
		||||
				if ( strcasecmp(accept_grub, "no") == 0 ||
 | 
			
		||||
					 strcasecmp(accept_grub, "yes") == 0 )
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
			if ( !strcasecmp(return_to_disked, "yes") )
 | 
			
		||||
				continue;
 | 
			
		||||
			text("Proceeding, but expect the installation to fail.\n");
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	textf("We are now ready to install %s %s. Take a moment to verify "
 | 
			
		||||
	      "everything is sane.\n", BRAND_DISTRIBUTION_NAME, VERSIONSTR);
 | 
			
		||||
	text("\n");
 | 
			
		||||
	printf("  %-16s  system architecture\n", uts.machine);
 | 
			
		||||
	for ( size_t i = 0; i < mountpoints_used; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		struct mountpoint* mnt = &mountpoints[i];
 | 
			
		||||
		const char* devname = path_of_blockdevice(mnt->fs->bdev);
 | 
			
		||||
		const char* where = mnt->entry.fs_file;
 | 
			
		||||
		printf("  %-16s  use as %s\n", devname, where);
 | 
			
		||||
	}
 | 
			
		||||
	if ( strcasecmp(accept_grub, "yes") == 0 )
 | 
			
		||||
	{
 | 
			
		||||
		struct partition* bbp = search_bios_boot_partition(root_filesystem);
 | 
			
		||||
		if ( bbp )
 | 
			
		||||
			printf("  %-16s  bios boot partition\n",
 | 
			
		||||
			       path_of_blockdevice(&bbp->bdev));
 | 
			
		||||
		printf("  %-16s  bootloader installation target\n",
 | 
			
		||||
		       device_path_of_blockdevice(root_filesystem->bdev));
 | 
			
		||||
	}
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	while ( true )
 | 
			
		||||
	{
 | 
			
		||||
		prompt(input, sizeof(input),
 | 
			
		||||
		       "Install " BRAND_DISTRIBUTION_NAME "? (yes/no)", "yes");
 | 
			
		||||
		if ( strcasecmp(input, "yes") != 0 )
 | 
			
		||||
		{
 | 
			
		||||
			text("Everything isn't sane? Answer '!' to get a shell or type ^C "
 | 
			
		||||
			     "to abort the install.\n");
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	text("Installing " BRAND_DISTRIBUTION_NAME " " VERSIONSTR " now:\n");
 | 
			
		||||
 | 
			
		||||
	printf(" - Mounting filesystems...\n");
 | 
			
		||||
 | 
			
		||||
	if ( !mkdtemp(fs) )
 | 
			
		||||
		err(2, "mkdtemp: %s", "/tmp/fs.XXXXXX");
 | 
			
		||||
	fs_made = true;
 | 
			
		||||
	setenv("SYSINSTALL_TARGET", fs, 1);
 | 
			
		||||
 | 
			
		||||
	for ( size_t i = 0; i < mountpoints_used; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		struct mountpoint* mnt = &mountpoints[i];
 | 
			
		||||
		char* absolute;
 | 
			
		||||
		if ( asprintf(&absolute, "%s%s", fs, mnt->absolute) < 0 )
 | 
			
		||||
			err(2, "asprintf");
 | 
			
		||||
		free(mnt->absolute);
 | 
			
		||||
		mnt->absolute = absolute;
 | 
			
		||||
		if ( mkdir_p(mnt->absolute, 0755) < 0 )
 | 
			
		||||
			err(2, "mkdir: %s", mnt->absolute);
 | 
			
		||||
		mountpoint_mount(mnt);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( chdir(fs) < 0 )
 | 
			
		||||
		err(2, "chdir: %s", fs);
 | 
			
		||||
 | 
			
		||||
	pid_t install_pid = fork();
 | 
			
		||||
	if ( install_pid < 0 )
 | 
			
		||||
		err(2, "fork");
 | 
			
		||||
	if ( install_pid == 0 )
 | 
			
		||||
	{
 | 
			
		||||
		printf(" - Populating root filesystem...\n");
 | 
			
		||||
		umask(0000);
 | 
			
		||||
		chmod(".", 0755);
 | 
			
		||||
		mkdir_or_chmod_or_die("bin", 0755);
 | 
			
		||||
		mkdir_or_chmod_or_die("boot", 0755);
 | 
			
		||||
		mkdir_or_chmod_or_die("dev", 0755);
 | 
			
		||||
		mkdir_or_chmod_or_die("etc", 0755);
 | 
			
		||||
		mkdir_or_chmod_or_die("etc/skel", 0755);
 | 
			
		||||
		mkdir_or_chmod_or_die("etc/init", 0755);
 | 
			
		||||
		mkdir_or_chmod_or_die("home", 0755);
 | 
			
		||||
		mkdir_or_chmod_or_die("include", 0755);
 | 
			
		||||
		mkdir_or_chmod_or_die("lib", 0755);
 | 
			
		||||
		mkdir_or_chmod_or_die("mnt", 0755);
 | 
			
		||||
		mkdir_or_chmod_or_die("root", 0700);
 | 
			
		||||
		mkdir_or_chmod_or_die("sbin", 0755);
 | 
			
		||||
		mkdir_or_chmod_or_die("share", 0755);
 | 
			
		||||
		mkdir_or_chmod_or_die("tix", 0755);
 | 
			
		||||
		mkdir_or_chmod_or_die("tix/manifest", 7555);
 | 
			
		||||
		mkdir_or_chmod_or_die("tmp", 01777);
 | 
			
		||||
		mkdir_or_chmod_or_die("var", 0755);
 | 
			
		||||
		mkdir_or_chmod_or_die("var/empty", 0555);
 | 
			
		||||
		umask(0022);
 | 
			
		||||
		if ( access("tix/collection.conf", F_OK) < 0 )
 | 
			
		||||
			execute((const char*[]) { "tix-collection", ".", "create",
 | 
			
		||||
		                              "--prefix=", NULL }, "_e");
 | 
			
		||||
		install_manifest("system", "", ".");
 | 
			
		||||
		// TODO: Preserve the existing /src if it exists like in sysupgrade.
 | 
			
		||||
		if ( has_manifest("src") )
 | 
			
		||||
			install_manifest("src", "", ".");
 | 
			
		||||
		printf(" - Creating configuration files...\n");
 | 
			
		||||
		// TODO: Preserve mode/ownership/timestamps?
 | 
			
		||||
		execute((const char*[]) { "cp", "-RTP", etc, "etc", NULL }, "_e");
 | 
			
		||||
		// TODO: Auto detect appropriate bcrypt rounds and set up etc/login.conf
 | 
			
		||||
		//       and use those below instead of blowfish,a.
 | 
			
		||||
		install_ports("", ".");
 | 
			
		||||
		printf(" - Creating initrd...\n");
 | 
			
		||||
		execute((const char*[]) { "update-initrd", "--sysroot", fs, NULL }, "_e");
 | 
			
		||||
		if ( strcasecmp(accept_grub, "yes") == 0 )
 | 
			
		||||
		{
 | 
			
		||||
			printf(" - Installing bootloader...\n");
 | 
			
		||||
			execute((const char*[]) { "chroot", "-d", ".", "grub-install",
 | 
			
		||||
			        device_path_of_blockdevice(root_filesystem->bdev), NULL },
 | 
			
		||||
			        "_eqQ");
 | 
			
		||||
			printf(" - Configuring bootloader...\n");
 | 
			
		||||
			execute((const char*[]) { "chroot", "-d", ".", "update-grub", NULL },
 | 
			
		||||
			        "_eqQ");
 | 
			
		||||
		}
 | 
			
		||||
		else if ( access_or_die("/etc/grub.d/10_sortix", F_OK) == 0 )
 | 
			
		||||
		{
 | 
			
		||||
			// Help dual booters by making /etc/grub.d/10_sortix.cache.
 | 
			
		||||
			printf(" - Creating bootloader fragment...\n");
 | 
			
		||||
			execute((const char*[]) { "chroot", "-d", ".",
 | 
			
		||||
			                          "/etc/grub.d/10_sortix", NULL }, "_eq");
 | 
			
		||||
		}
 | 
			
		||||
		printf(" - Finishing installation...\n");
 | 
			
		||||
		_exit(0);
 | 
			
		||||
	}
 | 
			
		||||
	int install_code;
 | 
			
		||||
	waitpid(install_pid, &install_code, 0);
 | 
			
		||||
	if ( WIFEXITED(install_code) && WEXITSTATUS(install_code) == 0 )
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
	else if ( WIFEXITED(install_code) )
 | 
			
		||||
		errx(2, "installation failed with exit status %i", WEXITSTATUS(install_code));
 | 
			
		||||
	else if ( WIFSIGNALED(install_code) )
 | 
			
		||||
		errx(2, "installation failed: %s", strsignal(WTERMSIG(install_code)));
 | 
			
		||||
	else
 | 
			
		||||
		errx(2, "installation failed: unknown waitpid code %i", install_code);
 | 
			
		||||
 | 
			
		||||
	unsetenv("SYSINSTALL_ETC");
 | 
			
		||||
	execute((const char*[]) { "rm", "-r", etc, NULL }, "");
 | 
			
		||||
	etc_made = false;
 | 
			
		||||
 | 
			
		||||
	text("\n");
 | 
			
		||||
	text("System files are now installed. We'll now make the system functional "
 | 
			
		||||
	     "by configuring a few essential matters.\n\n");
 | 
			
		||||
 | 
			
		||||
	umask(0022);
 | 
			
		||||
 | 
			
		||||
	if ( access("etc/hostname", F_OK) == 0 )
 | 
			
		||||
		textf("/etc/hostname already exists, skipping creating it.\n");
 | 
			
		||||
	else while ( true )
 | 
			
		||||
	{
 | 
			
		||||
		char defhost[HOST_NAME_MAX + 1] = "";
 | 
			
		||||
		FILE* defhost_fp = fopen("etc/hostname", "r");
 | 
			
		||||
		if ( defhost_fp )
 | 
			
		||||
		{
 | 
			
		||||
			fgets(defhost, sizeof(defhost), defhost_fp);
 | 
			
		||||
			size_t defhostlen = strlen(defhost);
 | 
			
		||||
			if ( defhostlen && defhost[defhostlen-1] == '\n' )
 | 
			
		||||
				defhost[defhostlen-1] = '\0';
 | 
			
		||||
			fclose(defhost_fp);
 | 
			
		||||
		}
 | 
			
		||||
		char hostname[HOST_NAME_MAX + 1] = "";
 | 
			
		||||
		prompt(hostname, sizeof(hostname), "System hostname?",
 | 
			
		||||
		       defhost[0] ? defhost : NULL);
 | 
			
		||||
		if ( !install_configurationf("etc/hostname", "w", "%s\n", hostname) )
 | 
			
		||||
			continue;
 | 
			
		||||
		textf("/etc/hostname was set to \"%s\".\n", hostname);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	if ( passwd_has_uid("etc/passwd", 0) ||
 | 
			
		||||
	     passwd_has_name("etc/passwd", "root") )
 | 
			
		||||
	{
 | 
			
		||||
		textf("Root account already exists, skipping creating it.\n");
 | 
			
		||||
	}
 | 
			
		||||
	else while ( true )
 | 
			
		||||
	{
 | 
			
		||||
		char first[128];
 | 
			
		||||
		char second[128];
 | 
			
		||||
		password(first, sizeof(first), "Password for root account? (will not echo)");
 | 
			
		||||
		password(second, sizeof(second), "Password for root account? (again)");
 | 
			
		||||
		if ( strcmp(first, second) != 0 )
 | 
			
		||||
		{
 | 
			
		||||
			printf("Passwords do not match, try again.\n");
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		explicit_bzero(second, sizeof(second));
 | 
			
		||||
		if ( !strcmp(first, "") )
 | 
			
		||||
		{
 | 
			
		||||
			char answer[32];
 | 
			
		||||
			prompt(answer, sizeof(answer),
 | 
			
		||||
			       "Empty password is stupid, are you sure? (yes/no)", "no");
 | 
			
		||||
			if ( strcasecmp(answer, "yes") != 0 )
 | 
			
		||||
				continue;
 | 
			
		||||
		}
 | 
			
		||||
		char hash[128];
 | 
			
		||||
		if ( crypt_newhash(first, "blowfish,a", hash, sizeof(hash)) < 0 )
 | 
			
		||||
		{
 | 
			
		||||
			explicit_bzero(first, sizeof(first));
 | 
			
		||||
			warn("crypt_newhash");
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		explicit_bzero(first, sizeof(first));
 | 
			
		||||
		if ( !install_configurationf("etc/passwd", "a",
 | 
			
		||||
				"root:%s:0:0:root:/root:sh\n", hash) )
 | 
			
		||||
			continue;
 | 
			
		||||
		textf("User '%s' added to /etc/passwd\n", "root");
 | 
			
		||||
		if ( !install_configurationf("etc/group", "a", "root::0:root\n") )
 | 
			
		||||
			continue;
 | 
			
		||||
		install_skel("/root", 0, 0);
 | 
			
		||||
		textf("Group '%s' added to /etc/group.\n", "root");
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	install_configurationf("etc/init/target", "w", "multi-user\n");
 | 
			
		||||
 | 
			
		||||
	text("Congratulations, the system is now functional! This is a good time "
 | 
			
		||||
	     "to do further customization of the system.\n\n");
 | 
			
		||||
 | 
			
		||||
	bool made_user = false;
 | 
			
		||||
	for ( uid_t uid = 1000; true; )
 | 
			
		||||
	{
 | 
			
		||||
		while ( passwd_has_uid("etc/passwd", uid) )
 | 
			
		||||
			uid++;
 | 
			
		||||
		gid_t gid = (gid_t) uid;
 | 
			
		||||
		static char userstr[256];
 | 
			
		||||
		const char* question = "Setup a user? (enter username or 'no')";
 | 
			
		||||
		if ( made_user )
 | 
			
		||||
			question = "Setup another user? (enter username or 'no')";
 | 
			
		||||
		prompt(userstr, sizeof(userstr), question, "no");
 | 
			
		||||
		if ( !strcmp(userstr, "no") )
 | 
			
		||||
			break;
 | 
			
		||||
		if ( !strcmp(userstr, "yes") )
 | 
			
		||||
			continue;
 | 
			
		||||
		const char* user = userstr;
 | 
			
		||||
		while ( user[0] == ' ')
 | 
			
		||||
			user++;
 | 
			
		||||
		if ( passwd_has_name("etc/passwd", user) )
 | 
			
		||||
		{
 | 
			
		||||
			textf("Account '%s' already exists.\n", user);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		static char name[256];
 | 
			
		||||
		prompt(name, sizeof(name), "Full name of user?", user);
 | 
			
		||||
		char first[128];
 | 
			
		||||
		char second[128];
 | 
			
		||||
		while ( true )
 | 
			
		||||
		{
 | 
			
		||||
			password(first, sizeof(first), "Password for user? (will not echo)");
 | 
			
		||||
			password(second, sizeof(second), "Password for user? (again)");
 | 
			
		||||
			if ( strcmp(first, second) != 0 )
 | 
			
		||||
			{
 | 
			
		||||
				printf("Passwords do not match, try again.\n");
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			explicit_bzero(second, sizeof(second));
 | 
			
		||||
			if ( !strcmp(first, "") )
 | 
			
		||||
			{
 | 
			
		||||
				char answer[32];
 | 
			
		||||
				prompt(answer, sizeof(answer),
 | 
			
		||||
					   "Empty password is stupid, are you sure? (yes/no)", "no");
 | 
			
		||||
				if ( strcasecmp(answer, "yes") != 0 )
 | 
			
		||||
					continue;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		char hash[128];
 | 
			
		||||
		if ( crypt_newhash(first, "blowfish,a", hash, sizeof(hash)) < 0 )
 | 
			
		||||
		{
 | 
			
		||||
			explicit_bzero(first, sizeof(first));
 | 
			
		||||
			warn("crypt_newhash");
 | 
			
		||||
			continue;unmount_all_but_root();
 | 
			
		||||
		}
 | 
			
		||||
		explicit_bzero(first, sizeof(first));
 | 
			
		||||
		if ( !install_configurationf("etc/passwd", "a",
 | 
			
		||||
				"%s:%s:%" PRIuUID ":%" PRIuGID ":%s:/home/%s:sh\n",
 | 
			
		||||
		        user, hash, uid, gid, name, user) )
 | 
			
		||||
			continue;
 | 
			
		||||
		if ( !install_configurationf("etc/group", "a",
 | 
			
		||||
				"%s::%" PRIuGID ":%s\n", user, gid, user) )
 | 
			
		||||
			continue;
 | 
			
		||||
		char* home;
 | 
			
		||||
		if ( asprintf(&home, "home/%s", user) < 0 )
 | 
			
		||||
		{
 | 
			
		||||
			warn("asprintf");
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if ( mkdir(home, 0700) < 0 && errno != EEXIST )
 | 
			
		||||
		{
 | 
			
		||||
			warn("mkdir: %s", home);
 | 
			
		||||
			free(home);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		chown(home, uid, gid);
 | 
			
		||||
		install_skel(home, uid, gid);
 | 
			
		||||
		free(home);
 | 
			
		||||
		textf("User '%s' added to /etc/passwd\n", user);
 | 
			
		||||
		textf("Group '%s' added to /etc/group.\n", user);
 | 
			
		||||
		text("\n");
 | 
			
		||||
		uid++;
 | 
			
		||||
		made_user = true;
 | 
			
		||||
	}
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	text("It's time to boot into the newly installed system.\n\n");
 | 
			
		||||
 | 
			
		||||
	if ( strcasecmp(accept_grub, "no") == 0 )
 | 
			
		||||
	{
 | 
			
		||||
		text("You did not accept a bootloader and need to arrange by "
 | 
			
		||||
		     "bootloading by your own means. /etc/grub.d/10_sortix.cache is a "
 | 
			
		||||
		     "GRUB configuration fragment that boots the newly installed "
 | 
			
		||||
		     "system.\n\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	text("Upon boot, you'll be greeted with a login screen. Enter your "
 | 
			
		||||
	     "credentials to get a command line. Login as user 'poweroff' as "
 | 
			
		||||
	     "described in login(8) to power off the machine.  After logging in, "
 | 
			
		||||
	     "type 'man user-guide' to view the introductory documentation.\n");
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	while ( true )
 | 
			
		||||
	{
 | 
			
		||||
		prompt(input, sizeof(input), "What now? (poweroff/reboot/boot)", "boot");
 | 
			
		||||
		if ( !strcasecmp(input, "poweroff") )
 | 
			
		||||
			exit(0);
 | 
			
		||||
		if ( !strcasecmp(input, "reboot") )
 | 
			
		||||
			exit(1);
 | 
			
		||||
		if ( !strcasecmp(input, "boot") )
 | 
			
		||||
		{
 | 
			
		||||
			unmount_all_but_root();
 | 
			
		||||
			unsetenv("SYSINSTALL_TARGET");
 | 
			
		||||
			unsetenv("SHLVL");
 | 
			
		||||
			unsetenv("INIT_PID");
 | 
			
		||||
			// TODO: If / is a kernel ramfs, and this is a live environment,
 | 
			
		||||
			//       then uninstall the base system to save memory.
 | 
			
		||||
			exit(execute((const char*[]) { "chroot", "-d", fs,
 | 
			
		||||
			                               "/sbin/init", NULL }, "f"));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										123
									
								
								sysinstall/sysmerge.8
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								sysinstall/sysmerge.8
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,123 @@
 | 
			
		|||
.Dd $Mdocdate: February 14 2016 $
 | 
			
		||||
.Dt SYSMERGE 8
 | 
			
		||||
.Os
 | 
			
		||||
.Sh NAME
 | 
			
		||||
.Nm sysmerge
 | 
			
		||||
.Nd upgrade current operating system from a sysroot
 | 
			
		||||
.Sh SYNOPSIS
 | 
			
		||||
.Nm sysmerge
 | 
			
		||||
.Op Fl w
 | 
			
		||||
.Op Fl \-booting
 | 
			
		||||
.Op Fl \-cancel
 | 
			
		||||
.Op Fl \-wait
 | 
			
		||||
.Ar source
 | 
			
		||||
.Sh DESCRIPTION
 | 
			
		||||
.Nm
 | 
			
		||||
upgrades the current operating system by copying the system files from the
 | 
			
		||||
specified
 | 
			
		||||
.Ar source
 | 
			
		||||
directory (usually a sysroot) onto the current root filesystem.  This is meant
 | 
			
		||||
to be used when building the operating system from source as described in
 | 
			
		||||
.Xr development 7 .
 | 
			
		||||
.Pp
 | 
			
		||||
.Nm
 | 
			
		||||
installs the
 | 
			
		||||
.Sy system
 | 
			
		||||
manifest from the tix repository in the
 | 
			
		||||
.Ar source
 | 
			
		||||
directory, as well as all the ports found.  The
 | 
			
		||||
.Xr initrd 7
 | 
			
		||||
is regenerated using
 | 
			
		||||
.Xr update-initrd 8 .
 | 
			
		||||
If grub is enabled in
 | 
			
		||||
.Xr upgrade.conf 5 ,
 | 
			
		||||
then the bootloader is reinstalled and reconfigured as needed.
 | 
			
		||||
.Pp
 | 
			
		||||
.Nm
 | 
			
		||||
is an automatic and non-interactive upgrade.  It is meant to be used as part of
 | 
			
		||||
the development process to upgrade to locally built versions.  It does not
 | 
			
		||||
contain compatibility to help upgrade across larger changes, or to easily handle
 | 
			
		||||
changes to configuration file formats, or other caveats.  It places a burden on
 | 
			
		||||
the system administrator/developer to know the system changes in detail and to
 | 
			
		||||
do any necessary tasks manually.
 | 
			
		||||
.Pp
 | 
			
		||||
The
 | 
			
		||||
.Xr sysupgrade 8
 | 
			
		||||
program is by contrast an interactive program, meant to help upgrading across
 | 
			
		||||
much larger development distances.  It contains compatibility to automatically
 | 
			
		||||
handle changes in configuration files and other larger changes.
 | 
			
		||||
.Pp
 | 
			
		||||
The options are as follows:
 | 
			
		||||
.Bl -tag -width "12345678"
 | 
			
		||||
.It Fl \-booting
 | 
			
		||||
It's boot time, complete the system upgrade that was delayed.  This is meant to
 | 
			
		||||
be used by
 | 
			
		||||
.Xr init 8
 | 
			
		||||
through the
 | 
			
		||||
.Sy chain-merge
 | 
			
		||||
boot target.  This installs the
 | 
			
		||||
.Pa /sysmerge
 | 
			
		||||
directory onto the root filesystem and removes the
 | 
			
		||||
.Pa /sysmerge
 | 
			
		||||
directory.
 | 
			
		||||
.It Fl \-cancel
 | 
			
		||||
Cancel a pending upgrade that would trigger on the next boot.  Remove the
 | 
			
		||||
.Pa /sysmerge
 | 
			
		||||
directory and restore the old
 | 
			
		||||
.Xr kernel 7
 | 
			
		||||
and
 | 
			
		||||
.Xr initrd 7 .
 | 
			
		||||
.It Fl w , Fl \-wait
 | 
			
		||||
Wait until the next boot to complete the upgrade, rather than finishing it now.
 | 
			
		||||
This installs into the
 | 
			
		||||
.Pa /sysmerge
 | 
			
		||||
directory instead and replaces the
 | 
			
		||||
.Xr kernel 7
 | 
			
		||||
with the new kernel
 | 
			
		||||
and
 | 
			
		||||
.Xr initrd 7
 | 
			
		||||
with an initrd that runs
 | 
			
		||||
.Sy /sysmerge/sbin/sysmerge --booting
 | 
			
		||||
on boot through the
 | 
			
		||||
.Sy chain-merge
 | 
			
		||||
.Xr init 8
 | 
			
		||||
boot target.  Backups are made of the
 | 
			
		||||
.Xr kernel 7
 | 
			
		||||
and
 | 
			
		||||
.Xr initrd 7
 | 
			
		||||
such that the operation can be rolled back.
 | 
			
		||||
.El
 | 
			
		||||
.Sh FILES
 | 
			
		||||
.Bl -tag -width "/boot/sortix.initrd.sysmerge.orig" -compact
 | 
			
		||||
.It Pa /boot/sortix.bin
 | 
			
		||||
system
 | 
			
		||||
.Xr kernel 7
 | 
			
		||||
.It Pa /boot/sortix.bin.sysmerge.orig
 | 
			
		||||
system
 | 
			
		||||
.Xr kernel 7
 | 
			
		||||
(backup)
 | 
			
		||||
.It Pa /boot/sortix.initrd
 | 
			
		||||
system
 | 
			
		||||
.Xr initrd 7
 | 
			
		||||
.It Pa /boot/sortix.initrd.sysmerge.orig
 | 
			
		||||
system
 | 
			
		||||
.Xr initrd 7
 | 
			
		||||
(backup)
 | 
			
		||||
.It Pa /etc/machine
 | 
			
		||||
processor platform of this installation
 | 
			
		||||
.It Pa /etc/sortix-release
 | 
			
		||||
the current system release
 | 
			
		||||
.It Pa /etc/upgrade.conf
 | 
			
		||||
controls the bootloader upgrade behavior (see
 | 
			
		||||
.Xr upgrade.conf 5 )
 | 
			
		||||
.It Pa /sysmerge
 | 
			
		||||
pending upgrade is stored here
 | 
			
		||||
.El
 | 
			
		||||
.Sh SEE ALSO
 | 
			
		||||
.Xr development 7 ,
 | 
			
		||||
.Xr initrd 7 ,
 | 
			
		||||
.Xr installation 7 ,
 | 
			
		||||
.Xr kernel 7 ,
 | 
			
		||||
.Xr upgrade 7 ,
 | 
			
		||||
.Xr sysinstall 8 ,
 | 
			
		||||
.Xr sysupgrade 8
 | 
			
		||||
							
								
								
									
										260
									
								
								sysinstall/sysmerge.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								sysinstall/sysmerge.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,260 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    sysmerge.c
 | 
			
		||||
    Upgrade current operating system from a sysroot.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
#include <err.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "conf.h"
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "fileops.h"
 | 
			
		||||
#include "manifest.h"
 | 
			
		||||
#include "release.h"
 | 
			
		||||
 | 
			
		||||
static void compact_arguments(int* argc, char*** argv)
 | 
			
		||||
{
 | 
			
		||||
	for ( int i = 0; i < *argc; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		while ( i < *argc && !(*argv)[i] )
 | 
			
		||||
		{
 | 
			
		||||
			for ( int n = i; n < *argc; n++ )
 | 
			
		||||
				(*argv)[n] = (*argv)[n+1];
 | 
			
		||||
			(*argc)--;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool has_pending_upgrade(void)
 | 
			
		||||
{
 | 
			
		||||
	return access_or_die("/boot/sortix.bin.sysmerge.orig", F_OK) == 0 ||
 | 
			
		||||
	       access_or_die("/boot/sortix.initrd.sysmerge.orig", F_OK) == 0 ||
 | 
			
		||||
	       access_or_die("/sysmerge", F_OK) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void help(FILE* fp, const char* argv0)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(fp, "Usage: %s [OPTION]... SOURCE\n", argv0);
 | 
			
		||||
	fprintf(fp, "Merge the files from SOURCE onto the current system.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
	setvbuf(stdout, NULL, _IOLBF, 0); // Pipes.
 | 
			
		||||
 | 
			
		||||
	bool cancel = false;
 | 
			
		||||
	bool booting = false;
 | 
			
		||||
	bool wait = false;
 | 
			
		||||
 | 
			
		||||
	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] != '-' )
 | 
			
		||||
		{
 | 
			
		||||
			char c;
 | 
			
		||||
			while ( (c = *++arg) ) switch ( c )
 | 
			
		||||
			{
 | 
			
		||||
			case 'w': wait = true; break;
 | 
			
		||||
			default:
 | 
			
		||||
				fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
 | 
			
		||||
				help(stderr, argv0);
 | 
			
		||||
				exit(1);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if ( !strcmp(arg, "--help") )
 | 
			
		||||
			help(stdout, argv0), exit(0);
 | 
			
		||||
		else if ( !strcmp(arg, "--version") )
 | 
			
		||||
			version(stdout, argv0), exit(0);
 | 
			
		||||
		else if ( !strcmp(arg, "--cancel") )
 | 
			
		||||
			cancel = true;
 | 
			
		||||
		else if ( !strcmp(arg, "--booting") )
 | 
			
		||||
			booting = true;
 | 
			
		||||
		else if ( !strcmp(arg, "--wait") )
 | 
			
		||||
			wait = true;
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
 | 
			
		||||
			help(stderr, argv0);
 | 
			
		||||
			exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	compact_arguments(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	const char* source;
 | 
			
		||||
	if ( cancel )
 | 
			
		||||
	{
 | 
			
		||||
		source = NULL;
 | 
			
		||||
		if ( 1 < argc )
 | 
			
		||||
			err(2, "Unexpected extra operand `%s'", argv[2]);
 | 
			
		||||
	}
 | 
			
		||||
	else if ( booting )
 | 
			
		||||
	{
 | 
			
		||||
		source = "/sysmerge";
 | 
			
		||||
		if ( 1 < argc )
 | 
			
		||||
			err(2, "Unexpected extra operand `%s'", argv[2]);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		if ( argc < 2 )
 | 
			
		||||
			err(2, "No source operand was given");
 | 
			
		||||
		source = argv[1];
 | 
			
		||||
		if ( 2 < argc )
 | 
			
		||||
			err(2, "Unexpected extra operand `%s'", argv[2]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( booting )
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
	else if ( has_pending_upgrade() )
 | 
			
		||||
	{
 | 
			
		||||
		rename("/boot/sortix.bin.sysmerge.orig", "/boot/sortix.bin");
 | 
			
		||||
		rename("/boot/sortix.initrd.sysmerge.orig", "/boot/sortix.initrd");
 | 
			
		||||
		execute((const char*[]) { "rm", "-rf", "/sysmerge", NULL }, "");
 | 
			
		||||
		execute((const char*[]) { "update-initrd", NULL }, "_e");
 | 
			
		||||
		printf("Cancelled pending system upgrade.\n");
 | 
			
		||||
	}
 | 
			
		||||
	else if ( cancel )
 | 
			
		||||
		printf("No system upgrade was pending.\n");
 | 
			
		||||
 | 
			
		||||
	if ( cancel )
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	const char* old_release_path = "/etc/sortix-release";
 | 
			
		||||
	struct release old_release;
 | 
			
		||||
	if ( !os_release_load(&old_release, old_release_path, old_release_path) )
 | 
			
		||||
		exit(2);
 | 
			
		||||
 | 
			
		||||
	char* new_release_path;
 | 
			
		||||
	if ( asprintf(&new_release_path, "%s/etc/sortix-release", source) < 0 )
 | 
			
		||||
		err(2, "asprintf");
 | 
			
		||||
	struct release new_release;
 | 
			
		||||
	if ( !os_release_load(&new_release, new_release_path, new_release_path) )
 | 
			
		||||
		exit(2);
 | 
			
		||||
	free(new_release_path);
 | 
			
		||||
 | 
			
		||||
	// TODO: Check if /etc/machine matches the current architecture.
 | 
			
		||||
	// TODO: Check for version (skipping, downgrading).
 | 
			
		||||
 | 
			
		||||
	struct conf conf;
 | 
			
		||||
	load_upgrade_conf(&conf, "/etc/upgrade.conf");
 | 
			
		||||
 | 
			
		||||
	bool can_run_old_abi = old_release.abi_major == new_release.abi_major &&
 | 
			
		||||
	                       old_release.abi_minor <= new_release.abi_minor;
 | 
			
		||||
	if ( !can_run_old_abi && !wait )
 | 
			
		||||
	{
 | 
			
		||||
		printf("Incompatible %lu.%lu -> %lu.%lu ABI transition, "
 | 
			
		||||
		       "delaying upgrade to next boot.\n",
 | 
			
		||||
		       old_release.abi_major, old_release.abi_major,
 | 
			
		||||
		       new_release.abi_major, new_release.abi_major);
 | 
			
		||||
		wait = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const char* target;
 | 
			
		||||
	if ( wait )
 | 
			
		||||
	{
 | 
			
		||||
		printf("Scheduling upgrade to %s on next boot using %s:\n",
 | 
			
		||||
		       new_release.pretty_name, source);
 | 
			
		||||
		target = "/sysmerge";
 | 
			
		||||
		if ( mkdir(target, 0755) < 0 )
 | 
			
		||||
			err(2, "%s", target);
 | 
			
		||||
		execute((const char*[]) { "tix-collection", "/sysmerge", "create",
 | 
			
		||||
		                          NULL }, "_e");
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		printf("Upgrading to %s using %s:\n", new_release.pretty_name, source);
 | 
			
		||||
		target = "";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	install_manifest("system", source, target);
 | 
			
		||||
	install_ports(source, target);
 | 
			
		||||
 | 
			
		||||
	if ( wait )
 | 
			
		||||
	{
 | 
			
		||||
		printf(" - Scheduling upgrade on next boot...\n");
 | 
			
		||||
		execute((const char*[]) { "cp", "/boot/sortix.bin",
 | 
			
		||||
		                                "/boot/sortix.bin.sysmerge.orig" }, "_e");
 | 
			
		||||
		execute((const char*[]) { "cp", "/boot/sortix.initrd",
 | 
			
		||||
		                                "/boot/sortix.initrd.sysmerge.orig" }, "_e");
 | 
			
		||||
		execute((const char*[]) { "cp", "/sysmerge/boot/sortix.bin",
 | 
			
		||||
		                                "/boot/sortix.bin" }, "_e");
 | 
			
		||||
		execute((const char*[]) { "/sysmerge/sbin/update-initrd", NULL }, "_e");
 | 
			
		||||
 | 
			
		||||
		printf("The system will be upgraded to %s on the next boot.\n",
 | 
			
		||||
		       new_release.pretty_name);
 | 
			
		||||
		printf("Run %s --cancel to cancel the upgrade.\n", argv[0]);
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( !wait && access_or_die("/etc/fstab", F_OK) == 0 )
 | 
			
		||||
	{
 | 
			
		||||
		printf(" - Creating initrd...\n");
 | 
			
		||||
		execute((const char*[]) { "update-initrd", NULL }, "_e");
 | 
			
		||||
 | 
			
		||||
		if ( conf.grub )
 | 
			
		||||
		{
 | 
			
		||||
			// TODO: Figure out the root device.
 | 
			
		||||
			//printf(" - Installing bootloader...\n");
 | 
			
		||||
			//execute((const char*[]) { "grub-install", "/", NULL }, "_eqQ");
 | 
			
		||||
			printf(" - Configuring bootloader...\n");
 | 
			
		||||
			execute((const char*[]) { "update-grub", NULL }, "_eqQ");
 | 
			
		||||
		}
 | 
			
		||||
		else if ( access_or_die("/etc/grub.d/10_sortix", F_OK) == 0 )
 | 
			
		||||
		{
 | 
			
		||||
			printf(" - Creating bootloader fragment...\n");
 | 
			
		||||
			execute((const char*[]) { "/etc/grub.d/10_sortix", NULL }, "_eq");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( booting )
 | 
			
		||||
	{
 | 
			
		||||
		unlink("/boot/sortix.bin.sysmerge.orig");
 | 
			
		||||
		unlink("/boot/sortix.initrd.sysmerge.orig");
 | 
			
		||||
		execute((const char*[]) { "rm", "-rf", "/sysmerge", NULL }, "");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	printf("Successfully upgraded to %s.\n", new_release.pretty_name);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								sysinstall/sysupgrade.8
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								sysinstall/sysupgrade.8
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,25 @@
 | 
			
		|||
.Dd $Mdocdate: January 5 2016 $
 | 
			
		||||
.Dt SYSUPGRADE 8
 | 
			
		||||
.Os
 | 
			
		||||
.Sh NAME
 | 
			
		||||
.Nm sysupgrade
 | 
			
		||||
.Nd operating system upgrader
 | 
			
		||||
.Sh SYNOPSIS
 | 
			
		||||
.Nm sysupgrade
 | 
			
		||||
.Sh DESCRIPTION
 | 
			
		||||
.Nm
 | 
			
		||||
is an interactive command line program that upgrades another installation to the
 | 
			
		||||
current operating system.  It asks basic questions, searches for existing
 | 
			
		||||
installations, verifies the upgrade makes sense, and upgrades the target system
 | 
			
		||||
according to
 | 
			
		||||
.Xr upgrade.conf 5
 | 
			
		||||
of the target system.  The upgrade proceeds as described in
 | 
			
		||||
.Xr upgrade 7 .
 | 
			
		||||
.Pp
 | 
			
		||||
.Nm
 | 
			
		||||
must be run as root with stdin, stdout and stderr all pointing to the terminal.
 | 
			
		||||
It is intended to be run from a live environment and not from an actual
 | 
			
		||||
installation.
 | 
			
		||||
.Sh SEE ALSO
 | 
			
		||||
.Xr upgrade 7 ,
 | 
			
		||||
.Xr sysinstall 8
 | 
			
		||||
							
								
								
									
										866
									
								
								sysinstall/sysupgrade.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										866
									
								
								sysinstall/sysupgrade.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,866 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 | 
			
		||||
    Copyright(C) Jonas 'Sortie' Termansen 2015, 2016.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License along with
 | 
			
		||||
    this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
    sysupgrade.c
 | 
			
		||||
    Operating system upgrader.
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <sys/display.h>
 | 
			
		||||
#include <sys/mount.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/utsname.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#include <brand.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <err.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fstab.h>
 | 
			
		||||
#include <sched.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <timespec.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include <mount/blockdevice.h>
 | 
			
		||||
#include <mount/filesystem.h>
 | 
			
		||||
#include <mount/harddisk.h>
 | 
			
		||||
#include <mount/partition.h>
 | 
			
		||||
 | 
			
		||||
#include "conf.h"
 | 
			
		||||
#include "devices.h"
 | 
			
		||||
#include "execute.h"
 | 
			
		||||
#include "fileops.h"
 | 
			
		||||
#include "interactive.h"
 | 
			
		||||
#include "manifest.h"
 | 
			
		||||
#include "release.h"
 | 
			
		||||
 | 
			
		||||
const char* prompt_man_section = "7";
 | 
			
		||||
const char* prompt_man_page = "upgrade";
 | 
			
		||||
 | 
			
		||||
struct installation
 | 
			
		||||
{
 | 
			
		||||
	struct blockdevice* bdev;
 | 
			
		||||
	struct release release;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct installation* installations;
 | 
			
		||||
static size_t installations_count;
 | 
			
		||||
static size_t installations_length;
 | 
			
		||||
 | 
			
		||||
static bool add_installation(struct blockdevice* bdev, struct release* release)
 | 
			
		||||
{
 | 
			
		||||
	if ( installations_count == installations_length )
 | 
			
		||||
	{
 | 
			
		||||
			size_t new_length = installations_length;
 | 
			
		||||
			if ( !new_length )
 | 
			
		||||
				new_length = 16;
 | 
			
		||||
			struct installation* new_installations = (struct installation*)
 | 
			
		||||
				reallocarray(NULL, new_length, sizeof(struct installation));
 | 
			
		||||
			if ( !new_installations )
 | 
			
		||||
				return false;
 | 
			
		||||
			installations = new_installations;
 | 
			
		||||
			installations_length = new_length;
 | 
			
		||||
	}
 | 
			
		||||
	struct installation* installation = &installations[installations_count++];
 | 
			
		||||
	installation->bdev = bdev;
 | 
			
		||||
	installation->release = *release;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void search_installation_path(const char* mnt, struct blockdevice* bdev)
 | 
			
		||||
{
 | 
			
		||||
	char* release_errpath;
 | 
			
		||||
	if ( asprintf(&release_errpath, "%s: /etc/sortix-release",
 | 
			
		||||
	              path_of_blockdevice(bdev)) < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		warn("malloc");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	char* release_path;
 | 
			
		||||
	if ( asprintf(&release_path, "%s/etc/sortix-release", mnt) < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		warn("malloc");
 | 
			
		||||
		free(release_errpath);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	struct release release;
 | 
			
		||||
	if ( os_release_load(&release, release_path, release_errpath) &&
 | 
			
		||||
	    !add_installation(bdev, &release) )
 | 
			
		||||
		release_free(&release);
 | 
			
		||||
	free(release_path);
 | 
			
		||||
	free(release_errpath);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: Switch to mountpoint_mount().
 | 
			
		||||
static bool await_mount(const char* mnt, pid_t pid, struct stat* oldst,
 | 
			
		||||
                        const char* bdev_path, const char* driver)
 | 
			
		||||
{
 | 
			
		||||
	while ( true )
 | 
			
		||||
	{
 | 
			
		||||
		struct stat newst;
 | 
			
		||||
		if ( stat(mnt, &newst) < 0 )
 | 
			
		||||
		{
 | 
			
		||||
			warn("%s", mnt);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		if ( newst.st_dev != oldst->st_dev || newst.st_ino != oldst->st_ino )
 | 
			
		||||
			break;
 | 
			
		||||
		int code;
 | 
			
		||||
		pid_t child = waitpid(pid, &code, WNOHANG);
 | 
			
		||||
		if ( child < 0 )
 | 
			
		||||
		{
 | 
			
		||||
			err(2, "waitpid");
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		if ( child != 0 )
 | 
			
		||||
		{
 | 
			
		||||
			if ( WIFSIGNALED(code) )
 | 
			
		||||
				warnx("%s: Mount failed: %s: %s", bdev_path, driver,
 | 
			
		||||
				      strsignal(WTERMSIG(code)));
 | 
			
		||||
			else if ( !WIFEXITED(code) )
 | 
			
		||||
				warnx("%s: Mount failed: %s: %s", bdev_path, driver,
 | 
			
		||||
				      "Unexpected unusual termination");
 | 
			
		||||
			else if ( WEXITSTATUS(code) == 127 )
 | 
			
		||||
				warnx("%s: Mount failed: %s: %s", bdev_path, driver,
 | 
			
		||||
				      "Filesystem driver is absent");
 | 
			
		||||
#if 0
 | 
			
		||||
			else if ( WEXITSTATUS(code) == 0 )
 | 
			
		||||
				warnx("%s: Mount failed: %s: Unexpected successful exit",
 | 
			
		||||
				      bdev_path, driver);
 | 
			
		||||
			else
 | 
			
		||||
				warnx("%s: Mount failed: %s: Exited with status %i", bdev_path,
 | 
			
		||||
				      driver, WEXITSTATUS(code));
 | 
			
		||||
#endif
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		struct timespec delay = timespec_make(0, 50L * 1000L * 1000L);
 | 
			
		||||
		nanosleep(&delay, NULL);
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pid_t begin_mount(const char* mnt, struct blockdevice* bdev)
 | 
			
		||||
{
 | 
			
		||||
	struct filesystem* fs = bdev->fs;
 | 
			
		||||
	if ( !fs )
 | 
			
		||||
		return -1;
 | 
			
		||||
	if ( !fs->driver )
 | 
			
		||||
		return -1;
 | 
			
		||||
	if ( fs->flags & FILESYSTEM_FLAG_FSCK_MUST && !fsck(fs) )
 | 
			
		||||
		return -1;
 | 
			
		||||
	struct stat fs_oldstat;
 | 
			
		||||
	if ( stat(mnt, &fs_oldstat) < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		warn("stat: %s", mnt);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	const char* bdev_path = path_of_blockdevice(fs->bdev);
 | 
			
		||||
	pid_t fs_pid = fork();
 | 
			
		||||
	if ( fs_pid < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		warn("fork");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	if ( fs_pid == 0 )
 | 
			
		||||
	{
 | 
			
		||||
		setpgid(0, 0);
 | 
			
		||||
		const char* argv[] =
 | 
			
		||||
		{
 | 
			
		||||
			fs->driver,
 | 
			
		||||
			"--foreground",
 | 
			
		||||
			bdev_path,
 | 
			
		||||
			mnt,
 | 
			
		||||
			NULL
 | 
			
		||||
		};
 | 
			
		||||
		execvp(argv[0], (char* const*) argv);
 | 
			
		||||
		warn("%s", argv[0]);
 | 
			
		||||
		_exit(127);
 | 
			
		||||
	}
 | 
			
		||||
	if ( !await_mount(mnt, fs_pid, &fs_oldstat, bdev_path, fs->driver) )
 | 
			
		||||
		return false;
 | 
			
		||||
	return fs_pid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void end_mount(const char* mnt, pid_t fs_pid)
 | 
			
		||||
{
 | 
			
		||||
	sched_yield();
 | 
			
		||||
	unmount(mnt, 0);
 | 
			
		||||
	waitpid(fs_pid, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void search_installation_bdev(const char* mnt, struct blockdevice* bdev)
 | 
			
		||||
{
 | 
			
		||||
	pid_t fs_pid = begin_mount(mnt, bdev);
 | 
			
		||||
	if ( fs_pid < 0 )
 | 
			
		||||
		return;
 | 
			
		||||
	search_installation_path(mnt, bdev);
 | 
			
		||||
	end_mount(mnt, fs_pid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void search_installations(const char* mnt)
 | 
			
		||||
{
 | 
			
		||||
	for ( size_t i = 0; i < installations_count; i++ )
 | 
			
		||||
		release_free(&installations[i].release);
 | 
			
		||||
	free(installations);
 | 
			
		||||
	installations_count = 0;
 | 
			
		||||
	installations_length = 0;
 | 
			
		||||
 | 
			
		||||
	for ( size_t i = 0; i < hds_count; i++ )
 | 
			
		||||
	{
 | 
			
		||||
		struct harddisk* hd = hds[i];
 | 
			
		||||
		if ( hd->bdev.pt )
 | 
			
		||||
		{
 | 
			
		||||
			for ( size_t n = 0; n < hd->bdev.pt->partitions_count; n++ )
 | 
			
		||||
			{
 | 
			
		||||
				struct partition* p = hd->bdev.pt->partitions[n];
 | 
			
		||||
				search_installation_bdev(mnt, &p->bdev);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			search_installation_bdev(mnt, &hd->bdev);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void next_version(const struct release* current, struct release* new)
 | 
			
		||||
{
 | 
			
		||||
	// Next release of a development snapshot is the final release.
 | 
			
		||||
	if ( current->version_dev )
 | 
			
		||||
	{
 | 
			
		||||
		new->version_major = current->version_major;
 | 
			
		||||
		new->version_minor = current->version_minor;
 | 
			
		||||
		new->version_dev = false;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Releases increment by 0.1.
 | 
			
		||||
	new->version_major = current->version_major;
 | 
			
		||||
	new->version_minor = current->version_minor + 1;
 | 
			
		||||
	new->version_dev = false;
 | 
			
		||||
 | 
			
		||||
	// Major increments instead of minor release 10.
 | 
			
		||||
	if ( new->version_minor == 10 )
 | 
			
		||||
	{
 | 
			
		||||
		new->version_major++;
 | 
			
		||||
		new->version_minor = 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool downgrading_version(const struct release* old,
 | 
			
		||||
                                const struct release* new)
 | 
			
		||||
{
 | 
			
		||||
	if ( new->version_major < old->version_major )
 | 
			
		||||
		return true;
 | 
			
		||||
	if ( new->version_major > old->version_major )
 | 
			
		||||
		return false;
 | 
			
		||||
	if ( new->version_major < old->version_major )
 | 
			
		||||
		return true;
 | 
			
		||||
	if ( new->version_major > old->version_major )
 | 
			
		||||
		return false;
 | 
			
		||||
	if ( new->version_dev && !old->version_dev )
 | 
			
		||||
		return true;
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool skipping_version(const struct release* old,
 | 
			
		||||
                             const struct release* new)
 | 
			
		||||
{
 | 
			
		||||
	// Not skipping a release if upgrading to older release.
 | 
			
		||||
	if ( downgrading_version(old, new) )
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	// Not skipping a release if upgrading to same release.
 | 
			
		||||
	if ( new->version_major == old->version_major &&
 | 
			
		||||
	     new->version_minor == old->version_minor &&
 | 
			
		||||
	     new->version_dev == old->version_dev )
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	// Not skipping a release if upgrading to the next release.
 | 
			
		||||
	struct release next;
 | 
			
		||||
	next_version(old, &next);
 | 
			
		||||
	if ( new->version_major == next.version_major &&
 | 
			
		||||
	     new->version_minor == next.version_minor )
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void preserve_src(const char* where)
 | 
			
		||||
{
 | 
			
		||||
	if ( access_or_die(where, F_OK) < 0 )
 | 
			
		||||
		return;
 | 
			
		||||
	if ( access_or_die("oldsrc", F_OK) < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		if ( mkdir("oldsrc", 0755) < 0 )
 | 
			
		||||
		{
 | 
			
		||||
			warn("oldsrc");
 | 
			
		||||
			_exit(1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	time_t now = time(NULL);
 | 
			
		||||
	struct tm tm;
 | 
			
		||||
	localtime_r(&now, &tm);
 | 
			
		||||
	char buf[64];
 | 
			
		||||
	snprintf(buf, sizeof(buf), "oldsrc/%s-%i-%02i-%02i",
 | 
			
		||||
	         where, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
 | 
			
		||||
	if ( access_or_die(buf, F_OK) == 0 )
 | 
			
		||||
	{
 | 
			
		||||
		snprintf(buf, sizeof(buf), "oldsrc/%s-%i-%02i-%02i-%02i-%02i-%02i",
 | 
			
		||||
		         where, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
 | 
			
		||||
		         tm.tm_hour, tm.tm_min, tm.tm_sec);
 | 
			
		||||
		if ( access_or_die(buf, F_OK) == 0 )
 | 
			
		||||
		{
 | 
			
		||||
			snprintf(buf, sizeof(buf), "oldsrc/%s.XXXXXX", where);
 | 
			
		||||
			if ( !mkdtemp(buf) )
 | 
			
		||||
			{
 | 
			
		||||
				warnx("failed to find location to store old /%s", where);
 | 
			
		||||
				_exit(1);
 | 
			
		||||
			}
 | 
			
		||||
			rmdir(buf);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	printf(" - Moving /%s to /%s\n", where, buf);
 | 
			
		||||
	if ( rename(where, buf) < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		warn("rename: %s -> %s", where, buf);
 | 
			
		||||
		_exit(1);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(void)
 | 
			
		||||
{
 | 
			
		||||
	shlvl();
 | 
			
		||||
 | 
			
		||||
	if ( !isatty(0) )
 | 
			
		||||
		errx(2, "fatal: stdin is not a terminal");
 | 
			
		||||
	if ( !isatty(1) )
 | 
			
		||||
		errx(2, "fatal: stdout is not a terminal");
 | 
			
		||||
	if ( !isatty(2) )
 | 
			
		||||
		errx(2, "fatal: stderr is not a terminal");
 | 
			
		||||
 | 
			
		||||
	if ( getuid() != 0 )
 | 
			
		||||
		errx(2, "You need to be root to install %s", BRAND_DISTRIBUTION_NAME);
 | 
			
		||||
	if ( getgid() != 0 )
 | 
			
		||||
		errx(2, "You need to be group root to install %s", BRAND_DISTRIBUTION_NAME);
 | 
			
		||||
 | 
			
		||||
	struct utsname uts;
 | 
			
		||||
	uname(&uts);
 | 
			
		||||
 | 
			
		||||
	static char input[256];
 | 
			
		||||
 | 
			
		||||
	textf("Hello and welcome to the " BRAND_DISTRIBUTION_NAME " " VERSIONSTR ""
 | 
			
		||||
	      " upgrader for %s.\n\n", uts.machine);
 | 
			
		||||
 | 
			
		||||
	// '|' rather than '||' is to ensure side effects.
 | 
			
		||||
	if ( missing_program("cut") |
 | 
			
		||||
	     missing_program("dash") |
 | 
			
		||||
	     missing_program("fsck.ext2") |
 | 
			
		||||
	     missing_program("grub-install") |
 | 
			
		||||
	     missing_program("man") |
 | 
			
		||||
	     missing_program("sed") |
 | 
			
		||||
	     missing_program("xargs") )
 | 
			
		||||
	{
 | 
			
		||||
		text("Warning: This system does not have the necessary third party "
 | 
			
		||||
		     "software installed to properly upgrade installations.\n");
 | 
			
		||||
		while ( true )
 | 
			
		||||
		{
 | 
			
		||||
			prompt(input, sizeof(input), "Sure you want to proceed?", "no");
 | 
			
		||||
			if ( strcasecmp(input, "no") == 0 )
 | 
			
		||||
				return 0;
 | 
			
		||||
			if ( strcasecmp(input, "yes") == 0 )
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		text("\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	text("This program will upgrade an existing installation to this "
 | 
			
		||||
	     "version. You can always escape to a shell by answering '!' to any "
 | 
			
		||||
	     "regular prompt. You can view the upgrade(7) manual page by answering "
 | 
			
		||||
	     "'!man'. Default answers are in []'s and can be selected by pressing "
 | 
			
		||||
	     "enter.\n\n");
 | 
			
		||||
 | 
			
		||||
	const char* readies[] =
 | 
			
		||||
	{
 | 
			
		||||
		"Ready",
 | 
			
		||||
		"Yes",
 | 
			
		||||
		"Yeah",
 | 
			
		||||
		"Yep",
 | 
			
		||||
		"Let's go",
 | 
			
		||||
		"Let's do this",
 | 
			
		||||
		"Betcha",
 | 
			
		||||
		"Sure am",
 | 
			
		||||
		"You bet",
 | 
			
		||||
		"This time it will listen to my music",
 | 
			
		||||
	};
 | 
			
		||||
	size_t num_readies = sizeof(readies) / sizeof(readies[0]);
 | 
			
		||||
	const char* ready = readies[arc4random_uniform(num_readies)];
 | 
			
		||||
	prompt(input, sizeof(input), "Ready?", ready);
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	while ( true )
 | 
			
		||||
	{
 | 
			
		||||
		// TODO: Detect the name of the current keyboard layout.
 | 
			
		||||
		prompt(input, sizeof(input),
 | 
			
		||||
		       "Choose your keyboard layout ('?' or 'L' for list)", "default");
 | 
			
		||||
		if ( !strcmp(input, "?") ||
 | 
			
		||||
		     !strcmp(input, "l") ||
 | 
			
		||||
		     !strcmp(input, "L") )
 | 
			
		||||
		{
 | 
			
		||||
			DIR* dir = opendir("/share/kblayout");
 | 
			
		||||
			if ( !dir )
 | 
			
		||||
			{
 | 
			
		||||
				warn("%s", "/share/kblayout");
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			bool space = false;
 | 
			
		||||
			struct dirent* entry;
 | 
			
		||||
			while ( (entry = readdir(dir)) )
 | 
			
		||||
			{
 | 
			
		||||
				if ( entry->d_name[0] == '.' )
 | 
			
		||||
					continue;
 | 
			
		||||
				if ( space )
 | 
			
		||||
					putchar(' ');
 | 
			
		||||
				fputs(entry->d_name, stdout);
 | 
			
		||||
				space = true;
 | 
			
		||||
			}
 | 
			
		||||
			closedir(dir);
 | 
			
		||||
			if ( !space )
 | 
			
		||||
				fputs("(No keyboard layouts available)", stdout);
 | 
			
		||||
			putchar('\n');
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if ( !strcmp(input, "default") )
 | 
			
		||||
			break;
 | 
			
		||||
		const char* argv[] = { "chkblayout", "--", input, NULL };
 | 
			
		||||
		if ( execute(argv, "f") == 0 )
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	struct dispmsg_get_driver_name dgdn = { 0 };
 | 
			
		||||
	dgdn.msgid = DISPMSG_GET_DRIVER_NAME;
 | 
			
		||||
	dgdn.device = 0;
 | 
			
		||||
	dgdn.driver_index = 0;
 | 
			
		||||
	dgdn.name.byte_size = 0;
 | 
			
		||||
	dgdn.name.str = NULL;
 | 
			
		||||
	if ( dispmsg_issue(&dgdn, sizeof(dgdn)) == 0 || errno != ENODEV )
 | 
			
		||||
	{
 | 
			
		||||
		while ( true )
 | 
			
		||||
		{
 | 
			
		||||
			prompt(input, sizeof(input),
 | 
			
		||||
			       "Select display resolution? (yes/no)", "yes");
 | 
			
		||||
			if ( strcasecmp(input, "no") && strcasecmp(input, "yes") )
 | 
			
		||||
				continue;
 | 
			
		||||
			if ( strcasecmp(input, "no") == 0 )
 | 
			
		||||
				break;
 | 
			
		||||
			if ( execute((const char*[]) { "chvideomode", NULL }, "f") != 0 )
 | 
			
		||||
				continue;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	struct release new_release;
 | 
			
		||||
	if ( !os_release_load(&new_release, "/etc/sortix-release",
 | 
			
		||||
	                                    "/etc/sortix-release") )
 | 
			
		||||
		exit(2);
 | 
			
		||||
 | 
			
		||||
	char mnt[] = "/tmp/fs.XXXXXX";
 | 
			
		||||
	if ( !mkdtemp(mnt) )
 | 
			
		||||
		err(2, "mkdtemp: %s", "/tmp/fs.XXXXXX");
 | 
			
		||||
 | 
			
		||||
	struct installation* target = NULL;
 | 
			
		||||
	while ( true )
 | 
			
		||||
	{
 | 
			
		||||
		text("Searching for existing installations...\n");
 | 
			
		||||
		scan_devices();
 | 
			
		||||
		search_installations(mnt);
 | 
			
		||||
		text("\n");
 | 
			
		||||
 | 
			
		||||
		if ( installations_count == 0 )
 | 
			
		||||
		{
 | 
			
		||||
			while ( true)
 | 
			
		||||
			{
 | 
			
		||||
				prompt(input, sizeof(input), "No existing installations found, "
 | 
			
		||||
					   "run installer instead? (yes/no)", "yes");
 | 
			
		||||
				if ( !strcasecmp(input, "no") || !strcasecmp(input, "yes") )
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
			if ( !strcasecmp(input, "yes") )
 | 
			
		||||
			{
 | 
			
		||||
				text("\n");
 | 
			
		||||
				rmdir(mnt);
 | 
			
		||||
				execlp("sysinstall", "sysinstall", (const char*) NULL);
 | 
			
		||||
				warn("sysinstall");
 | 
			
		||||
				if ( !mkdtemp(mnt) )
 | 
			
		||||
					err(2, "mkdtemp: %s", "/tmp/fs.XXXXXX");
 | 
			
		||||
				text("\n");
 | 
			
		||||
			}
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		while ( true )
 | 
			
		||||
		{
 | 
			
		||||
			for ( size_t i = 0; i < installations_count; i++ )
 | 
			
		||||
			{
 | 
			
		||||
				struct installation* installation = &installations[i];
 | 
			
		||||
				printf("  %-16s  %s\n",
 | 
			
		||||
					   path_of_blockdevice(installation->bdev),
 | 
			
		||||
					   installation->release.pretty_name);
 | 
			
		||||
			}
 | 
			
		||||
			text("\n");
 | 
			
		||||
 | 
			
		||||
			const char* def = NULL;
 | 
			
		||||
			if ( installations_count == 1 )
 | 
			
		||||
				def = path_of_blockdevice(installations[0].bdev);
 | 
			
		||||
			prompt(input, sizeof(input), "Which installation to upgrade?", def);
 | 
			
		||||
			target = NULL;
 | 
			
		||||
			for ( size_t i = 0; i < installations_count; i++ )
 | 
			
		||||
			{
 | 
			
		||||
				struct installation* installation = &installations[i];
 | 
			
		||||
				const char* path = path_of_blockdevice(installation->bdev);
 | 
			
		||||
				if ( strcmp(input, path) != 0 )
 | 
			
		||||
					continue;
 | 
			
		||||
				target = installation;
 | 
			
		||||
			}
 | 
			
		||||
			if ( !target )
 | 
			
		||||
			{
 | 
			
		||||
				text("Answer was not one of the found devices.\n\n");
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	struct release* target_release = &target->release;
 | 
			
		||||
 | 
			
		||||
	// TODO: Check if /etc/machine matches the current architecture.
 | 
			
		||||
	// TODO: Some 1.0dev systems don't have /etc/machine, assume it matches if
 | 
			
		||||
	//       absent. Remove this compatibility  after releasing Sortix 1.0.
 | 
			
		||||
 | 
			
		||||
	if ( downgrading_version(target_release, &new_release) )
 | 
			
		||||
	{
 | 
			
		||||
		text("Warning: You are downgrading an existing installation to an "
 | 
			
		||||
		     "earlier release. This is not supported and there is no promise "
 | 
			
		||||
		     "this will work!\n\n");
 | 
			
		||||
 | 
			
		||||
		while ( true)
 | 
			
		||||
		{
 | 
			
		||||
			prompt(input, sizeof(input),
 | 
			
		||||
			       "Downgrade to an earlier release?", "no");
 | 
			
		||||
			if ( !strcasecmp(input, "no") || !strcasecmp(input, "yes") )
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		if ( !strcasecmp(input, "no") )
 | 
			
		||||
			errx(2, "Upgrade aborted due to version downgrade");
 | 
			
		||||
		text("\n");
 | 
			
		||||
	}
 | 
			
		||||
	else if ( skipping_version(target_release, &new_release) )
 | 
			
		||||
	{
 | 
			
		||||
		text("Warning: You are not upgrading this installation to its next "
 | 
			
		||||
		     "release. You cannot skip releases. This is not supported and "
 | 
			
		||||
		     "there is no promise this will will work!\n\n");
 | 
			
		||||
 | 
			
		||||
		while ( true )
 | 
			
		||||
		{
 | 
			
		||||
			prompt(input, sizeof(input),
 | 
			
		||||
			       "Skip across releases?", "no");
 | 
			
		||||
			if ( !strcasecmp(input, "no") || !strcasecmp(input, "yes") )
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		if ( !strcasecmp(input, "no") )
 | 
			
		||||
			errx(2, "Upgrade aborted due to skipping releases");
 | 
			
		||||
		text("\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( new_release.abi_major < target_release->abi_major ||
 | 
			
		||||
	     (target_release->abi_major == new_release.abi_major &&
 | 
			
		||||
	      new_release.abi_minor < target_release->abi_minor) )
 | 
			
		||||
	{
 | 
			
		||||
		text("Warning: You are downgrading an existing installation to an "
 | 
			
		||||
		     "release with an earlier ABI. This is not supported and there is "
 | 
			
		||||
		     "no promise this will work!\n\n");
 | 
			
		||||
 | 
			
		||||
		while ( true)
 | 
			
		||||
		{
 | 
			
		||||
			prompt(input, sizeof(input),
 | 
			
		||||
			       "Downgrade to an earlier ABI?", "no");
 | 
			
		||||
			if ( !strcasecmp(input, "no") || !strcasecmp(input, "yes") )
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		if ( !strcasecmp(input, "no") )
 | 
			
		||||
			errx(2, "Upgrade aborted due to ABI downgrade");
 | 
			
		||||
		text("\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool can_run_old_abi = target_release->abi_major == new_release.abi_major &&
 | 
			
		||||
	                       target_release->abi_minor <= new_release.abi_minor;
 | 
			
		||||
 | 
			
		||||
	struct blockdevice* bdev = target->bdev;
 | 
			
		||||
	const char* bdev_path = path_of_blockdevice(bdev);
 | 
			
		||||
 | 
			
		||||
	pid_t fs_pid = begin_mount(mnt, bdev);
 | 
			
		||||
	if ( fs_pid < 0 )
 | 
			
		||||
		err(2, "mounting %s at %s", bdev_path, mnt);
 | 
			
		||||
 | 
			
		||||
	if ( chdir(mnt) < 0 )
 | 
			
		||||
		err(2, "%s", mnt);
 | 
			
		||||
 | 
			
		||||
	if ( access_or_die("sysmerge", F_OK) == 0 )
 | 
			
		||||
	{
 | 
			
		||||
		text("Warning: A sysmerge(8) upgrade is scheduled for the next boot. "
 | 
			
		||||
		     "You must cancel this to proceed.\n\n");
 | 
			
		||||
		if ( !can_run_old_abi )
 | 
			
		||||
		{
 | 
			
		||||
			text("Error: Can't pending upgrade due to ABI change.\n");
 | 
			
		||||
			errx(2, "Upgrade aborted due to pending sysmerge(8) upgrade");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		while ( true )
 | 
			
		||||
		{
 | 
			
		||||
			prompt(input, sizeof(input),
 | 
			
		||||
			       "Cancel pending sysmerge upgrade?", "yes");
 | 
			
		||||
			if ( !strcasecmp(input, "no") || !strcasecmp(input, "yes") )
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
		if ( !strcasecmp(input, "no") )
 | 
			
		||||
			errx(2, "Upgrade aborted due to pending sysmerge(8) upgrade");
 | 
			
		||||
		text("\n");
 | 
			
		||||
		execute((const char*[]) { "chroot", "-d", "sysmerge", "--cancel", NULL }, "e");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO: Remove after releasing Sortix 1.0.
 | 
			
		||||
	if ( target_release->version_major == 1 &&
 | 
			
		||||
	     target_release->version_minor == 0 &&
 | 
			
		||||
	     target_release->version_dev &&
 | 
			
		||||
	     access_or_die("etc/upgrade.conf", F_OK) < 0 )
 | 
			
		||||
	{
 | 
			
		||||
		text("1.0dev compatibility: Creating /etc/upgrade.conf\n");
 | 
			
		||||
		FILE* fp = fopen("etc/upgrade.conf", "w");
 | 
			
		||||
		if ( !fp )
 | 
			
		||||
			err(2, "etc/upgrade.conf");
 | 
			
		||||
		if ( access_or_die("src", F_OK) == 0 )
 | 
			
		||||
			fprintf(fp,  "src = yes\n");
 | 
			
		||||
		if ( access_or_die("boot/grub/grub.cfg", F_OK) == 0 )
 | 
			
		||||
			fprintf(fp,  "grub = yes\n");
 | 
			
		||||
		if ( ferror(fp) || fflush(fp) == EOF )
 | 
			
		||||
			err(2, "etc/upgrade.conf");
 | 
			
		||||
		if ( fclose(fp) == EOF )
 | 
			
		||||
			err(2, "etc/upgrade.conf");
 | 
			
		||||
		text("\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	struct conf conf;
 | 
			
		||||
	while ( true )
 | 
			
		||||
	{
 | 
			
		||||
		load_upgrade_conf(&conf, "etc/upgrade.conf");
 | 
			
		||||
 | 
			
		||||
		textf("We are now ready to upgrade to %s %s. Take a moment to verify "
 | 
			
		||||
			  "everything is sane.\n", BRAND_DISTRIBUTION_NAME, VERSIONSTR);
 | 
			
		||||
		text("\n");
 | 
			
		||||
		char abibuf[16];
 | 
			
		||||
		printf("  %-16s  system architecture\n", uts.machine);
 | 
			
		||||
		printf("  %-16s  root filesystem\n", bdev_path);
 | 
			
		||||
		printf("  %-16s  old version\n", target_release->pretty_name);
 | 
			
		||||
		printf("  %-16s  new version\n", new_release.pretty_name);
 | 
			
		||||
		snprintf(abibuf, sizeof(abibuf), "%lu.%lu",
 | 
			
		||||
			     target_release->abi_major, target_release->abi_minor);
 | 
			
		||||
		printf("  %-16s  old ABI\n", abibuf);
 | 
			
		||||
		snprintf(abibuf, sizeof(abibuf), "%lu.%lu",
 | 
			
		||||
			     new_release.abi_major, new_release.abi_minor);
 | 
			
		||||
		printf("  %-16s  new ABI\n", abibuf);
 | 
			
		||||
		if ( conf.system )
 | 
			
		||||
			printf("  %-16s  will be updated\n", "system");
 | 
			
		||||
		else
 | 
			
		||||
			printf("  %-16s  will not be updated\n", "system");
 | 
			
		||||
		if ( conf.ports )
 | 
			
		||||
			printf("  %-16s  will be updated\n", "ports");
 | 
			
		||||
		else
 | 
			
		||||
			printf("  %-16s  will not be updated\n", "ports");
 | 
			
		||||
		if ( has_manifest("src") )
 | 
			
		||||
		{
 | 
			
		||||
			if ( conf.newsrc )
 | 
			
		||||
				printf("  %-16s  new source code\n", "/newsrc");
 | 
			
		||||
			else if ( conf.src )
 | 
			
		||||
				printf("  %-16s  will be updated\n", "/src");
 | 
			
		||||
			else
 | 
			
		||||
				printf("  %-16s  will not be updated\n", "/src");
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			printf("  %-16s  will not be updated\n", "/src");
 | 
			
		||||
		text("\n");
 | 
			
		||||
 | 
			
		||||
		prompt(input, sizeof(input),
 | 
			
		||||
		       "Upgrade? (yes/no)", "yes");
 | 
			
		||||
		if ( strcasecmp(input, "yes") != 0 )
 | 
			
		||||
		{
 | 
			
		||||
			text("Everything isn't sane? Answer '!' to get a shell or type ^C "
 | 
			
		||||
			     "to abort the upgrade.\n");
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	// TODO: Switch to local time zone of the existing system?
 | 
			
		||||
 | 
			
		||||
	text("Upgrading to " BRAND_DISTRIBUTION_NAME " " VERSIONSTR " now:\n");
 | 
			
		||||
 | 
			
		||||
	pid_t upgrade_pid = fork();
 | 
			
		||||
	if ( upgrade_pid < 0 )
 | 
			
		||||
		err(2, "fork");
 | 
			
		||||
	if ( upgrade_pid == 0 )
 | 
			
		||||
	{
 | 
			
		||||
		umask(0022);
 | 
			
		||||
		// TODO: Use an upgrade manifest system that notices files that are now
 | 
			
		||||
		//       untracked or moved from one manifest to another.
 | 
			
		||||
		if ( conf.system )
 | 
			
		||||
			install_manifest("system", "", ".");
 | 
			
		||||
		if ( has_manifest("src") )
 | 
			
		||||
		{
 | 
			
		||||
			if ( conf.newsrc )
 | 
			
		||||
			{
 | 
			
		||||
				bool has_src = access_or_die("src", F_OK) == 0;
 | 
			
		||||
				if ( has_src )
 | 
			
		||||
				{
 | 
			
		||||
					preserve_src("newsrc");
 | 
			
		||||
					if ( rename("src", "src.tmp") < 0 )
 | 
			
		||||
					{
 | 
			
		||||
						warn("rename: /src -> /src.tmp");
 | 
			
		||||
						_exit(1);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				install_manifest("src", "", ".");
 | 
			
		||||
				if ( has_src )
 | 
			
		||||
				{
 | 
			
		||||
					if ( rename("src", "newsrc") < 0 )
 | 
			
		||||
					{
 | 
			
		||||
						warn("rename: /src -> /newsrc");
 | 
			
		||||
						_exit(1);
 | 
			
		||||
					}
 | 
			
		||||
					if ( rename("src.tmp", "src") < 0 )
 | 
			
		||||
					{
 | 
			
		||||
						warn("rename: /src.tmp -> /src");
 | 
			
		||||
						_exit(1);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else if ( conf.src )
 | 
			
		||||
			{
 | 
			
		||||
				preserve_src("src");
 | 
			
		||||
				install_manifest("src", "", ".");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if ( conf.ports )
 | 
			
		||||
			install_ports("", ".");
 | 
			
		||||
		if ( conf.system )
 | 
			
		||||
		{
 | 
			
		||||
			printf(" - Creating initrd...\n");
 | 
			
		||||
			execute((const char*[]) { "update-initrd", "--sysroot", mnt, NULL }, "_e");
 | 
			
		||||
		}
 | 
			
		||||
		if ( (conf.ports || (conf.system && can_run_old_abi)) && conf.grub )
 | 
			
		||||
		{
 | 
			
		||||
			printf(" - Installing bootloader...\n");
 | 
			
		||||
			execute((const char*[]) { "chroot", "-d", ".", "grub-install",
 | 
			
		||||
			        device_path_of_blockdevice(bdev), NULL },
 | 
			
		||||
			        "_eqQ");
 | 
			
		||||
			printf(" - Configuring bootloader...\n");
 | 
			
		||||
			execute((const char*[]) { "chroot", "-d", ".", "update-grub", NULL },
 | 
			
		||||
			        "_eqQ");
 | 
			
		||||
		}
 | 
			
		||||
		else if ( conf.system &&
 | 
			
		||||
		          access_or_die("/etc/grub.d/10_sortix", F_OK) == 0 )
 | 
			
		||||
		{
 | 
			
		||||
			// Help dual booters by making /etc/grub.d/10_sortix.cache.
 | 
			
		||||
			printf(" - Creating bootloader fragment...\n");
 | 
			
		||||
			execute((const char*[]) { "chroot", "-d", ".",
 | 
			
		||||
			                          "/etc/grub.d/10_sortix", NULL }, "_eq");
 | 
			
		||||
		}
 | 
			
		||||
		printf(" - Finishing upgrade...\n");
 | 
			
		||||
		_exit(0);
 | 
			
		||||
	}
 | 
			
		||||
	int upgrade_code;
 | 
			
		||||
	waitpid(upgrade_pid, &upgrade_code, 0);
 | 
			
		||||
	if ( WIFEXITED(upgrade_code) && WEXITSTATUS(upgrade_code) == 0 )
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
	else if ( WIFEXITED(upgrade_code) )
 | 
			
		||||
		errx(2, "upgrade failed with exit status %i", WEXITSTATUS(upgrade_code));
 | 
			
		||||
	else if ( WIFSIGNALED(upgrade_code) )
 | 
			
		||||
		errx(2, "upgrade failed: %s", strsignal(WTERMSIG(upgrade_code)));
 | 
			
		||||
	else
 | 
			
		||||
		errx(2, "upgrade failed: unknown waitpid code %i", upgrade_code);
 | 
			
		||||
	text("\n");
 | 
			
		||||
 | 
			
		||||
	if ( chdir("/") < 0 )
 | 
			
		||||
		err(2, "%s", "/");
 | 
			
		||||
 | 
			
		||||
	end_mount(mnt, fs_pid);
 | 
			
		||||
 | 
			
		||||
	if ( conf.system )
 | 
			
		||||
		textf("The %s installation has now been upgraded to %s.\n\n",
 | 
			
		||||
		      bdev_path, new_release.pretty_name);
 | 
			
		||||
	else if ( conf.newsrc )
 | 
			
		||||
		textf("The %s installation now contains the new source code in /newsrc. "
 | 
			
		||||
		      "You need to build it as described in development(7).\n\n",
 | 
			
		||||
		      bdev_path);
 | 
			
		||||
	else if ( conf.src )
 | 
			
		||||
		textf("The %s installation now contains the new source code in /src. "
 | 
			
		||||
		      "You need to build it as described in development(7).\n\n",
 | 
			
		||||
		      bdev_path);
 | 
			
		||||
	else
 | 
			
		||||
		textf("The %s installation has been upgraded to %s as requested.\n\n",
 | 
			
		||||
		      bdev_path, new_release.pretty_name);
 | 
			
		||||
 | 
			
		||||
	if ( target_release->abi_major < new_release.abi_major )
 | 
			
		||||
	{
 | 
			
		||||
		text("Note: The system has been upgraded across a major ABI change. "
 | 
			
		||||
		     "Locally compiled programs must be recompiled as they no longer "
 | 
			
		||||
		     "can be expected to work.\n\n");
 | 
			
		||||
	}
 | 
			
		||||
	else if ( target_release->abi_major == new_release.abi_major &&
 | 
			
		||||
	          target_release->abi_minor < new_release.abi_minor )
 | 
			
		||||
	{
 | 
			
		||||
		text("Note: The system has been upgraded across a minor ABI change.\n\n");
 | 
			
		||||
	}
 | 
			
		||||
	else if ( new_release.abi_major < target_release->abi_major ||
 | 
			
		||||
	          (target_release->abi_major == new_release.abi_major &&
 | 
			
		||||
	           new_release.abi_minor < target_release->abi_minor) )
 | 
			
		||||
	{
 | 
			
		||||
		text("Note: The system has been downgraded to an earlier ABI. "
 | 
			
		||||
		     "Locally compiled programs must be recompiled as they no longer "
 | 
			
		||||
		     "can be expected to work.\n\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while ( true )
 | 
			
		||||
	{
 | 
			
		||||
		prompt(input, sizeof(input), "What now? (poweroff/reboot)", "reboot");
 | 
			
		||||
		if ( !strcasecmp(input, "poweroff") )
 | 
			
		||||
			return 0;
 | 
			
		||||
		if ( !strcasecmp(input, "reboot") )
 | 
			
		||||
			return 1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -78,17 +78,27 @@ if [ ! -e "$sysroot/etc/fstab" ]; then
 | 
			
		|||
  echo "$0: $sysroot/etc/fstab: Need a filesystem table to make an initrd" >&2
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
sysmerge=false
 | 
			
		||||
exec_prefix="$sysroot"
 | 
			
		||||
if [ -d "$sysroot/sysmerge" ]; then
 | 
			
		||||
  sysmerge=true
 | 
			
		||||
  exec_prefix="$sysroot/sysmerge"
 | 
			
		||||
fi
 | 
			
		||||
tmp=$(mktemp -d)
 | 
			
		||||
trap 'rm -rf "$tmp"' EXIT HUP INT QUIT TERM
 | 
			
		||||
mkdir "$tmp/bin"
 | 
			
		||||
mkdir "$tmp/sbin"
 | 
			
		||||
cp "$sysroot/sbin/init" "$tmp/sbin"
 | 
			
		||||
cp "$sysroot/sbin/extfs" "$tmp/sbin"
 | 
			
		||||
test -f "$sysroot/sbin/fsck.ext2" &&
 | 
			
		||||
cp "$sysroot/sbin/fsck.ext2" "$tmp/sbin"
 | 
			
		||||
cp "$exec_prefix/sbin/init" "$tmp/sbin"
 | 
			
		||||
cp "$exec_prefix/sbin/extfs" "$tmp/sbin"
 | 
			
		||||
test -f "$exec_prefix/sbin/fsck.ext2" &&
 | 
			
		||||
cp "$exec_prefix/sbin/fsck.ext2" "$tmp/sbin"
 | 
			
		||||
mkdir "$tmp/etc"
 | 
			
		||||
cp "$sysroot/etc/fstab" "$tmp/etc/fstab"
 | 
			
		||||
mkdir "$tmp/etc/init"
 | 
			
		||||
echo chain > "$tmp/etc/init/target"
 | 
			
		||||
if $sysmerge; then
 | 
			
		||||
  echo chain-merge > "$tmp/etc/init/target"
 | 
			
		||||
else
 | 
			
		||||
  echo chain > "$tmp/etc/init/target"
 | 
			
		||||
fi
 | 
			
		||||
mkdir -p "$sysroot/boot"
 | 
			
		||||
mkinitrd --format=sortix-initrd-2 "$tmp" -o "$sysroot/boot/sortix.initrd" > /dev/null
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue