/*
 * The new sysinstall program.
 *
 * This is probably the last program in the `sysinstall' line - the next
 * generation being essentially a complete rewrite.
 *
 * $FreeBSD: src/release/sysinstall/installUpgrade.c,v 1.61.2.8 1999/12/15 01:32:28 jkh Exp $
 *
 * Copyright (c) 1995
 *	Jordan Hubbard.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer,
 *    verbatim and that no modifications are made prior to this
 *    point in the file.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include "sysinstall.h"
#include <sys/disklabel.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/mount.h>

static int installUpgradeNonInteractive(dialogMenuItem *self);

typedef struct _hitList {
    enum { JUST_COPY, CALL_HANDLER } action ;
    char *name;
    Boolean optional;
    void (*handler)(struct _hitList *self);
} HitList;

/* These are the only meaningful files I know about */
static HitList etc_files [] = {
   { JUST_COPY,		"Xaccel.ini",		TRUE, NULL },
   { JUST_COPY,		"XF86Config",		TRUE, NULL },
   { JUST_COPY,		"adduser.conf",		TRUE, NULL },
   { JUST_COPY,		"aliases",		TRUE, NULL },
   { JUST_COPY,		"aliases.db",		TRUE, NULL },
   { JUST_COPY,		"amd.map",		TRUE, NULL },
   { JUST_COPY,		"auth.conf",		TRUE, NULL },
   { JUST_COPY,		"crontab",		TRUE, NULL },
   { JUST_COPY,		"csh.cshrc",		TRUE, NULL },
   { JUST_COPY,		"csh.login",		TRUE, NULL },
   { JUST_COPY,		"csh.logout",		TRUE, NULL },
   { JUST_COPY,		"cvsupfile",		TRUE, NULL },
   { JUST_COPY,		"disktab",		TRUE, NULL },
   { JUST_COPY,		"dm.conf",		TRUE, NULL },
   { JUST_COPY,		"dumpdates",		TRUE, NULL },
   { JUST_COPY,		"exports",		TRUE, NULL },
   { JUST_COPY,		"fbtab",		TRUE, NULL },
   { JUST_COPY,		"fstab",		FALSE, NULL },
   { JUST_COPY,		"ftpusers",		TRUE, NULL },
   { JUST_COPY,		"gettytab",		TRUE, NULL },
   { JUST_COPY,		"gnats",		TRUE, NULL },
   { JUST_COPY,		"group",		FALSE, NULL },
   { JUST_COPY,		"login.access",		TRUE, NULL },
   { JUST_COPY,		"host.conf",		TRUE, NULL },
   { JUST_COPY,		"hosts",		TRUE, NULL },
   { JUST_COPY,		"hosts.equiv",		TRUE, NULL },
   { JUST_COPY,		"hosts.lpd",		TRUE, NULL },
   { JUST_COPY,		"inetd.conf",		TRUE, NULL },
   { JUST_COPY,		"kerberosIV",		TRUE, NULL },
   { JUST_COPY,		"localtime",		TRUE, NULL },
   { JUST_COPY,		"login.access",		TRUE, NULL },
   { JUST_COPY,		"mail.rc",		TRUE, NULL },
   { JUST_COPY,		"make.conf",		TRUE, NULL },
   { JUST_COPY,		"manpath.config",	TRUE, NULL },
   { JUST_COPY,		"master.passwd",	FALSE, NULL },
   { JUST_COPY,		"modems",		TRUE, NULL },
   { JUST_COPY,		"motd",			TRUE, NULL },
   { JUST_COPY,		"namedb",		TRUE, NULL },
   { JUST_COPY,		"networks",		TRUE, NULL },
   { JUST_COPY,		"newsyslog.conf",	TRUE, NULL },
   { JUST_COPY,		"pam.conf",		TRUE, NULL },
   { JUST_COPY,		"passwd",		TRUE, NULL },
   { JUST_COPY,		"periodic",		TRUE, NULL },
   { JUST_COPY,		"ppp",			TRUE, NULL },
   { JUST_COPY,		"printcap",		TRUE, NULL },
   { JUST_COPY,		"profile",		TRUE, NULL },
   { JUST_COPY,		"pwd.db",		TRUE, NULL },
   { JUST_COPY,		"rc.local",		TRUE, NULL },
   { JUST_COPY,		"rc.firewall",		TRUE, NULL },
   { JUST_COPY,		"rc.conf.local",	TRUE, NULL },
   { JUST_COPY,		"remote",		TRUE, NULL },
   { JUST_COPY,		"resolv.conf",		TRUE, NULL },
   { JUST_COPY,		"rmt",			TRUE, NULL },
   { JUST_COPY,		"sendmail.cf",		TRUE, NULL },
   { JUST_COPY,		"sendmail.cw",		TRUE, NULL },
   { JUST_COPY,		"services",		TRUE, NULL },
   { JUST_COPY,		"shells",		TRUE, NULL },
   { JUST_COPY,		"skeykeys",		TRUE, NULL },
   { JUST_COPY,		"spwd.db",		TRUE, NULL },
   { JUST_COPY,		"syslog.conf",		TRUE, NULL },
   { JUST_COPY,		"ttys",			TRUE, NULL },
   { JUST_COPY,		"uucp",			TRUE, NULL },
   { 0 },
};

void
traverseHitlist(HitList *h)
{
    system("rm -rf /etc/upgrade");
    Mkdir("/etc/upgrade");
    while (h->name) {
	if (!file_readable(h->name)) {
	    if (!h->optional) {
		KANJI_MESSAGE(
		"以前の /etc/%s ファイルが見つかりません!\n"
		"これによって, 多少おかしなアップグレードが行なわれるかも知れません.");
		msgConfirm(KM("Unable to find an old /etc/%s file!  That is decidedly non-standard and\n"
			   "your upgraded system may function a little strangely as a result."), h->name);
	    }
	}
	else {
	    if (h->action == JUST_COPY) {
		/* Move the just-loaded copy aside */
		vsystem("mv /etc/%s /etc/upgrade/%s", h->name, h->name);

		/* Copy the old one into its place */
		KANJI_MESSAGE("%s を復活させています..");
		msgNotify(KM("Resurrecting %s.."), h->name);
		/* Do this with tar so that symlinks and such are preserved */
		if (vsystem("tar cf - %s | tar xpf - -C /etc", h->name)) {
		    KANJI_MESSAGE("以前の /etc/%s を復活できません!  うーん.");
		    msgConfirm(KM("Unable to resurrect your old /etc/%s!  Hmmmm."), h->name);
		}
	    }
	    else /* call handler */
		h->handler(h);
	}
	++h;
    }
}

int
installUpgrade(dialogMenuItem *self)
{
    char saved_etc[FILENAME_MAX];
    Boolean extractingBin = TRUE;

    if (variable_get(VAR_NONINTERACTIVE))
	return installUpgradeNonInteractive(self);

    variable_set2(SYSTEM_STATE, "upgrade", 0);
#if 0
    /* systemDisplayHelp("UPGRADE"); NEW */
#else
    systemDisplayHelp("upgrade");
#endif

    KANJI_MESSAGE(
    "いろいろな恐ろしげな警告がされているはずですが, それでも本当にアッ\n"
    "プグレードのリスクを受け入れるのですね?");
    if (msgYesNo(KM("Given all that scary stuff you just read, are you sure you want to\n"
		 "risk it all and proceed with this upgrade?")) != 0)
	return DITEM_FAILURE;

    if (!Dists) {
	KANJI_MESSAGE(
	"最初に, 配布コンポーネントを選んでください. アップグレードの場合は\n"
	"次のメニューセットで指定された配布のみがアップグレードされます.");
	msgConfirm(KM("First, you must select some distribution components.  The upgrade procedure\n"
		   "will only upgrade the distributions you select in the next set of menus."));
	if (!dmenuOpenSimple(&MenuDistributions, FALSE) || !Dists)
	    return DITEM_FAILURE;
    }
    /* else */	/* delete N. Koganemaru */
    if (!(Dists & DIST_BIN)) {	    /* No bin selected?  Not much of an upgrade.. */
	KANJI_MESSAGE(
	"読み込むファイルとして配布ファイル bin を選択していません. これは\n"
	"確実にアップグレードするのに非常に重要なファイルです. 本当に bin\n"
	"配布ファイルなしにアップグレードしてもいいのですか? もし配布ファイ\n"
	"ルの選択メニューに再び戻る場合は No を選択して下さい.");
	if (msgYesNo(KM("You didn't select the bin distribution as one of the distributons to load.\n"
		     "This one is pretty vital to a successful upgrade.  Are you SURE you don't\n"
		     "want to select the bin distribution?  Chose No to bring up the Distributions\n"
		     "menu again.")) != 0) {
	    if (!dmenuOpenSimple(&MenuDistributions, FALSE))
		return DITEM_FAILURE;
	}
    }
    if (!(Dists & DIST_BIN))	    /* No bin selected?  Not much of an upgrade.. */
	return DITEM_FAILURE | DITEM_RESTORE;
#ifdef PC98
    if (!(Dists & DIST_98BIN)) {	/* No 98bin selected?  Not much of an upgrade.. */
	KANJI_MESSAGE(
	"読み込むファイルとして配布ファイル 98bin を選択していません. これは\n"
	"確実にアップグレードするのに非常に重要なファイルです. 本当に 98bin\n"
	"配布ファイルなしにアップグレードしてもいいのですか? もし配布ファイ\n"
	"ルの選択メニューに再び戻る場合は No を選択して下さい.");
	if (msgYesNo(KM("You didn't select the 98bin distribution as one of the distributons to load.\n"
		     "This one is pretty vital to a successful upgrade.  Are you SURE you don't\n"
		     "want to select the 98bin distribution?  Chose No to bring up the Distributions\n"
		     "menu again.")) != 0) {
	    if (!dmenuOpenSimple(&MenuDistributions, FALSE))
		return DITEM_FAILURE | DITEM_RESTORE;
	    dialog_clear_norefresh();
	}
    }
    if (!(Dists & DIST_98BIN))		/* No 98bin selected?  Not much of an upgrade.. */
	return DITEM_FAILURE | DITEM_RESTORE;
#endif

    /* Still?!  OK!  They must know what they're doing.. */
#ifdef PC98
    if (!(Dists & (DIST_BIN|DIST_98BIN)))
	extractingBin = FALSE;
#else
    if (!(Dists & DIST_BIN))
	extractingBin = FALSE;
#endif

    if (RunningAsInit) {
	Device **devs;
	int i, cnt;
	char *cp;

	cp = variable_get(VAR_DISK);
	devs = deviceFind(cp, DEVICE_TYPE_DISK);
	cnt = deviceCount(devs);
	if (!cnt) {
	    KANJI_MESSAGE(
	    "ディスクが見つかりません. 起動時に, ハードディスクコントローラが\n"
	    "正しく認識されているか確認して下さい. ドキュメントメニューのハード\n"
	    "ウェアガイドが, この種の問題解決の手がかりとなります.");
	    msgConfirm(KM("No disks found!  Please verify that your disk controller is being\n"
		       "properly probed at boot time.  See the Hardware Guide on the\n"
		       "Documentation menu for clues on diagnosing this type of problem."));
	    return DITEM_FAILURE;
	}
	else {
	    /* Enable all the drives before we start */
	    for (i = 0; i < cnt; i++)
		devs[i]->enabled = TRUE;
	}

	KANJI_MESSAGE(
	"はい, ではまず, ディスクラベルエディタに移動します. このエディタで\n"
	"は, アップグレードの対象となる全てのパーティションのマウント指定\n"
	"を行なう必要があります. 完全に確信の持てる操作でないのであれば,\n"
	"「絶対に」 newfs フラグを Y に設定しないで下さい. この場合には, ラ\n"
	"ベルエディタを画面指向のパーティションのマウント・ツールとして\n"
	"使用することになります. ラベルエディタでの処理が終ったら,\n"
	"Q を押してこの場所に戻り, 次のステップに移りましょう.");
	msgConfirm(KM("OK.  First, we're going to go to the disk label editor.  In this editor\n"
		   "you will be expected to Mount any partitions you're interested in\n"
		   "upgrading.  DO NOT set the Newfs flag to Y on anything in the label editor\n"
		   "unless you're absolutely sure you know what you're doing!  In this\n"
		   "instance, you'll be using the label editor as little more than a fancy\n"
		   "screen-oriented partition mounting tool.\n\n"
		   "Once you're done in the label editor, press Q to return here for the next\n"
		   "step."));

	if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE) {
	    KANJI_MESSAGE("ディスクラベルエディタがエラー・ステータスを返しました.\n"
		       "アップグレードを中断します.");
	    msgConfirm(KM("The disk label editor returned an error status.  Upgrade operation\n"
		       "aborted."));
	    return DITEM_FAILURE;
	}

	/* Don't write out MBR info */
	variable_set2(DISK_PARTITIONED, "written", 0);
	if (DITEM_STATUS(diskLabelCommit(self)) == DITEM_FAILURE) {
	    KANJI_MESSAGE("一部のファイルシステムを正常にマウントできませんでした.\n"
		       "アップグレードを中断します.");
	    msgConfirm(KM("Not all file systems were properly mounted.  Upgrade operation\n"
		       "aborted."));
	    variable_unset(DISK_PARTITIONED);
	    return DITEM_FAILURE;
	}

	KANJI_MESSAGE("ルートファイルシステムの /stand を更新しています");
	msgNotify(KM("Updating /stand on root filesystem"));
	(void)vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());

	if (DITEM_STATUS(chroot("/mnt")) == DITEM_FAILURE) {
	    KANJI_MESSAGE("/mnt に chroot できません. ルートパーティションに異常があるか,\n"
			  "マウント法に問題があるかという原因でしょう.");
	    msgConfirm(KM("Unable to chroot to /mnt - something is wrong with the\n"
		       "root partition or the way it's mounted if this doesn't work."));
	    variable_unset(DISK_PARTITIONED);
	    return DITEM_FAILURE;
	}
	chdir("/");
	installEnvironment();
	systemCreateHoloshell();
    }

    saved_etc[0] = '\0';

    /* Don't allow sources to be upgraded unless if we have src already */
#if defined(PC98) && defined(PC98SRC)
    if (directory_exists("/usr/src/") && (Dists & (DIST_SRC|DIST_98SRC)))
#else
    if (directory_exists("/usr/src/") && (Dists & DIST_SRC))
#endif /* defined(PC98) && defined(PC98SRC) */
    {
#if defined(PC98) && defined(PC98SRC)
	Dists &= ~(DIST_SRC|DIST_98SRC);
	SrcDists = Src98Dists = 0;
#else
	Dists &= ~DIST_SRC;
	SrcDists = 0;
#endif /* defined(PC98) && defined(PC98SRC) */
	KANJI_MESSAGE(
		    "警告: アップグレードターゲットとしてソースが選択されましたが,\n"
		    "/usr/src ディレクトリが存在します. ソースのアップグレードは\n"
		    "不成功です - CTM か CVSup か ``deletion events'' 操作の他の方\n"
		    "法を使ってください.\n\n"
		    "/usr/src ディレクトリはこのアップグレードで影響を受けません.");
	msgConfirm(KM("Warning: /usr/src exists and sources were selected as upgrade\n"
		   "targets.  Unfortunately, this is not the way to upgrade your\n"
		   "sources - please use CTM or CVSup or some other method which\n"
		   "handles ``deletion events'', unlike this particular feature.\n\n"
		   "Your existing /usr/src will not be affected by this upgrade.\n"));
    }

    if (extractingBin) {
	while (!*saved_etc) {
	    char *cp;
	    KANJI_MESSAGE("どこのディレクトリに現在の /etc の内容を保存しますか?");
	    cp = msgGetInput("/usr/tmp/etc", KM("Under which directory do you wish to save your current /etc?"));

	    if (!cp || !*cp || Mkdir(cp)) {
		KANJI_MESSAGE(
		"ディレクトリが指定されていない, あるいは不正であるか, キャンセルが\n"
		"選択されました. /etc のバックアップをとらずにアップグレードするこ\n"
		"とは全くお勧めできません. もう一度保存ディレクトリの設定画面に戻り\n"
		"ますか?");
		if (msgYesNo(KM("Directory was not specified, was invalid or user selected Cancel.\n\n"
			     "Doing an upgrade without first backing up your /etc directory is a very\n"
			     "bad idea!  Do you want to go back and specify the save directory again?")) != 0)
		    break;
	    }
	    else {
		SAFE_STRCPY(saved_etc, cp);
	    }
	}

	if (saved_etc[0]) {
	    KANJI_MESSAGE("/etc ディレクトリを保存しています…");
	    msgNotify(KM("Preserving /etc directory.."));
	    if (vsystem("tar -cBpf - -C /etc . | tar --unlink -xBpf - -C %s", saved_etc))
	    	KANJI_MESSAGE("/etc の内容を %s にバックアップできません.\n"
		"それでも続行しますか?");
		if (msgYesNo(KM("Unable to backup your /etc into %s.\n"
			     "Do you want to continue anyway?"), saved_etc) != 0)
		    return DITEM_FAILURE;
	    KANJI_MESSAGE("/root ディレクトリを保存しています…");
	    msgNotify(KM("Preserving /root directory.."));
	    vsystem("tar -cBpf - -C / root | tar --unlink -xBpf - -C %s", saved_etc);
	}

	KANJI_MESSAGE("元からあるバイナリを chflag しています - 少しお待ち下さい.");
	msgNotify(KM("chflags'ing old binaries - please wait."));
	(void)vsystem("chflags -R noschg /bin /sbin /usr/sbin /usr/bin /usr/lib /kernel*");

	if (file_readable("/kernel")) {
	    KANJI_MESSAGE("以前のカーネルを /kernel.prev に移動しています");
	    msgNotify(KM("Moving old kernel to /kernel.prev"));
	    if (system("mv /kernel /kernel.prev")) {
		KANJI_MESSAGE(
		"あれ!  昔のカーネルを移動できません. これはあなたにとって大きな問\n"
		"題ですか? そうであるならばアップグレードを中断しましょう. このアッ\n"
		"プグレード作業の仕様により, 中断するにはもう一度最初から再起動する\n"
		"必要があります. もし今, 再起動するならば, Yes を選択して下さい.");
		if (!msgYesNo(KM("Hmmm!  I couldn't move the old kernel over!  Do you want to\n"
			      "treat this as a big problem and abort the upgrade?  Due to the\n"
			      "way that this upgrade process works, you will have to reboot\n"
			      "and start over from the beginning.  Select Yes to reboot now")))
		    systemShutdown(1);
	    }
	    else {
		KANJI_MESSAGE(
		"注意: 古い kernel は /kernel.prev になっています. 何らかの理由\n"
		"でアップグレードが失敗した場合は古いカーネルでブートする必要が\n"
		"あります");
		msgConfirm(KM("NOTICE: Your old kernel is in /kernel.prev should this upgrade\n"
			   "fail for any reason and you need to boot your old kernel"));
	    }
	}
    }

media:
    /* We do this very late, but we unfortunately need to back up /etc first */
    if (!mediaVerify())
	return DITEM_FAILURE;

    if (!mediaDevice->init(mediaDevice)) {
	KANJI_MESSAGE("メディアを初期化できません. メディア選択をやり直して\n"
		      "再挑戦しますか?");
	if (!msgYesNo(KM("Couldn't initialize the media.  Would you like\n"
		   "to adjust your media selection and try again?"))) {
	    mediaDevice = NULL;
	    goto media;
	}
	else
	    return DITEM_FAILURE | DITEM_REDRAW;
    }

    KANJI_MESSAGE("配布ファイルの展開を開始します…");
    msgNotify(KM("Beginning extraction of distributions.."));
    if (DITEM_STATUS(distExtractAll(self)) == DITEM_FAILURE) {
    	KANJI_MESSAGE(
    	"あれ. bin 配布ファイルを展開できません. このアップグレードは失敗\n"
    	"です. 最初からやり直す必要があります. システムをリブートします.");
	msgConfirm(KM("Hmmmm.  We couldn't even extract the bin distribution.  This upgrade\n"
		   "should be considered a failure and started from the beginning, sorry!\n"
		   "The system will reboot now."));
	dialog_clear();
	systemShutdown(1);
    }
    else if (Dists) {
#ifdef PC98
	if (!extractingBin || !(Dists & (DIST_BIN|DIST_98BIN))) {
	    KANJI_MESSAGE(
	    "展開作業にはいくつかの問題が生じましたが, 終了しました. しかし, 最\n"
	    "も重要なファイルに関しては成功しています. それほど重要ではないファ\n"
	    "イルについては失敗しているという点で, このメッセージは警告であると\n"
	    "考えます.");
	    msgNotify(KM("The extraction process seems to have had some problems, but we got most\n"
		       "of the essentials.  We'll treat this as a warning since it may have been\n"
		       "only non-essential distributions which failed to load."));
	}
	else {
	    KANJI_MESSAGE(
	    "あれ. bin か 98bin 配布ファイルを展開できません. このアップグレード\n"
	    "は失敗です. 最初からやり直す必要があります. システムをリブートします.");
	    msgConfirm(KM("Hmmmm.  We couldn't even extract the bin or 98bin distribution.  This\n"
		       "upgrade should be considered a failure and started from the beginning, sorry!\n"
		       "The system will reboot now."));
	    dialog_clear();
	    systemShutdown(1);
	}
#else
	if (!extractingBin || !(Dists & DIST_BIN)) {
	    KANJI_MESSAGE(
	    "展開作業にはいくつかの問題が生じましたが, 終了しました. しかし, 最\n"
	    "も重要なファイルに関しては成功しています. それほど重要ではないファ\n"
	    "イルについては失敗しているという点で, このメッセージは警告であると\n"
	    "考えます.");
	    msgNotify(KM("The extraction process seems to have had some problems, but we got most\n"
		       "of the essentials.  We'll treat this as a warning since it may have been\n"
		       "only non-essential distributions which failed to load."));
	}
	else {
	    KANJI_MESSAGE(
	    "あれ. bin 配布ファイルを展開できません. このアップグレードは失敗\n"
	    "です. 最初からやり直す必要があります. システムをリブートします.");
	    msgConfirm(KM("Hmmmm.  We couldn't even extract the bin distribution.  This upgrade\n"
		       "should be considered a failure and started from the beginning, sorry!\n"
		       "The system will reboot now."));
	    dialog_clear();
	    systemShutdown(1);
	}
#endif
    }

#if 0
    if (extractingBin) {
	KANJI_MESSAGE(
	"はい, 今から新しいシステムのための全ての /dev エントリを作るために\n"
	"しばらく時間がかかります. また, bin 配布ファイルの上書きに関するい\n"
	"くつかの「補修」作業を行ないます…");
	msgNotify(KM("OK, now it's time to go pound on your root a little bit to create all the\n"
		  "/dev entries and such that a new system expects to see.  I'll also perform a\n"
		  "few \"fixup\" operations to repair the effects of splatting a bin distribution\n"
		  "on top of an existing system.."));
	if (DITEM_STATUS(installFixup(self)) == DITEM_FAILURE) {
	    KANJI_MESSAGE(
	    "あれ, 補修作業には多少問題が生じたようです. /etc のカスタマイズを\n"
	    "復旧する際にシステムを少し詳細にチェックしましょう.");
	    msgConfirm(KM("Hmmmmm.  The fixups don't seem to have been very happy.\n"
		       "You may wish to examine the system a little more closely when\n"
		       "it comes time to merge your /etc customizations back."));
	}
    }
#endif

    if (extractingBin)
	vsystem("disklabel -B `awk '$2~/\\/$/ {print substr($1, 6, 3)}' /etc/fstab`");
    KANJI_MESSAGE(
    "アップグレードの最初の段階は正常に終了しました!\n"
    "第二段階の /etc ディレクトリの復旧に移ります.");
    msgNotify(KM("First stage of upgrade completed successfully!\n\n"
	       "Next comes stage 2, where we attempt to resurrect your /etc\n"
	       "directory!"));

    if (saved_etc && chdir(saved_etc)) {
	KANJI_MESSAGE(
	"/etc のバックアップを保存しておいたディレクトリ %s に移れません?!\n"
	"何か深刻な問題が生じています. 以前の /etc が失われる可能性が非常に\n"
	"高いと思われます. ここにとても重要なカスタマイズをあなたが行なって\n"
	"いないということを祈ります :(");
	msgConfirm(KM("Unable to go to your saved /etc directory in %s?!  Argh!\n"
		   "Something went seriously wrong!  It's quite possible that\n"
		   "your former /etc is toast.  I hope you didn't have any\n"
		   "important customizations you wanted to keep in there.. :("), saved_etc);
    }
    else {
	/* Now try to resurrect the /etc files */
	traverseHitlist(etc_files);
	/* Resurrect the root dotfiles */
	vsystem("tar -cBpf - root | tar -xBpf - -C / && rm -rf root");
    }

#if 0	/* OLD */
#ifdef PC98
    KANJI_MESSAGE(
    "オーケイ! この段階で, 全ての /etc ファイルの復活は完了しました. \n"
    "そして新しいファイルは参照用に /etc/upgrade/<file> にコピーされて\n"
    "います. ここでそれらチェックのために緊急用ホログラフィック・シェル\n"
    " [GRPH-F4] るのが良いでしょう. 新しいシステムで再起動する準備が出来\n"
    "たらインストールを終了して下さい.");
    msgConfirm(KM("OK!  At this stage, we've resurrected all the /etc files\n"
	       "and moved each new copy over to /etc/upgrade/<file> in case you want\n"
	       "to see what the new versions look like.  If you want to wander over\n"
	       "to the Emergency Holographic Shell [GRPH-F4] at this point to do\n"
	       "that, now would be a good time.  When you're ready to reboot into\n"
	       "the new system, just exit the installation."));
#else
    KANJI_MESSAGE(
    "オーケイ! この段階で, 全ての /etc ファイルの復活は完了しました. \n"
    "そして新しいファイルは参照用に /etc/upgrade/<file> にコピーされて\n"
    "います. ここでそれらチェックのために緊急用ホログラフィック・シェル\n"
    " [ALT-F4] るのが良いでしょう. 新しいシステムで再起動する準備が出来\n"
    "たらインストールを終了して下さい.");
    msgConfirm(KM("OK!  At this stage, we've resurrected all the /etc files\n"
	       "and moved each new copy over to /etc/upgrade/<file> in case you want\n"
	       "to see what the new versions look like.  If you want to wander over\n"
	       "to the Emergency Holographic Shell [ALT-F4] at this point to do\n"
	       "that, now would be a good time.  When you're ready to reboot into\n"
	       "the new system, just exit the installation."));
#endif
#endif
    KANJI_MESSAGE(
    "アップグレードは完了しました! 全ての古い /etc ファイルはリストアさ\n"
    "れました. 新しい /etc ファイルは /etc/upgrade/ にあります. (厳密に\n"
    "は必要がありませんが)手動でこれらのファイルをアップグレードする場合\n"
    "に参照します. /etc/fstab にルートパーティションが記述されているなら\n"
    "古い\"コンパチビリティ\"スライスが使われています. スタートアップ時にウ\n"
    "ォーニングがでないように適格なスライス名を使うように修正できます.\n"
    "新しいシステムをリブートできる準備ができたら, 単にインストレーション\n"
    "を終了してください.");
    msgConfirm(KM("Upgrade completed!  All of your old /etc files have been restored.\n"
	       "For your reference, the new /etc files are in /etc/upgrade/ in case\n"
	       "you wish to upgrade these files by hand (though that should not be\n"
	       "strictly necessary).  If your root partition is specified in /etc/fstab\n"
	       "using the old \"compatibility\" slice, you may also wish to update it to\n"
	       "use a fully qualified slice name in order to avoid warnings on startup.\n\n"
	       "When you're ready to reboot into the new system, simply exit the installation."));
    return DITEM_SUCCESS | DITEM_REDRAW;
}

static int
installUpgradeNonInteractive(dialogMenuItem *self)
{
    char *saved_etc;
    Boolean extractingBin = TRUE;

    variable_set2(SYSTEM_STATE, "upgrade", 0);

    /* Make sure at least BIN is selected */
    Dists |= DIST_BIN;
#ifdef PC98
    Dists |= DIST_98BIN;
#endif

    if (RunningAsInit) {
	Device **devs;
	int i, cnt;
	char *cp;

	cp = variable_get(VAR_DISK);
	devs = deviceFind(cp, DEVICE_TYPE_DISK);
	cnt = deviceCount(devs);
	if (!cnt) {
	    KANJI_MESSAGE(
	    "ディスクが見つかりません. ブート時にディスクコントローラが正常に検\n"
	    "出されていることを確認してください. 文書メニュー中のハードウェアガ\n"
	    "イドが, この種のトラブルを診断するときの助けとなるでしょう.");
	    msgConfirm(KM("No disks found!  Please verify that your disk controller is being\n"
		       "properly probed at boot time.  See the Hardware Guide on the\n"
		       "Documentation menu for clues on diagnosing this type of problem."));
	    return DITEM_FAILURE;
	}
	else {
	    /* Enable all the drives befor we start */
	    for (i = 0; i < cnt; i++)
		devs[i]->enabled = TRUE;
	}

	KANJI_MESSAGE(
	"はい, ではまず, ディスクラベルエディタに移動します. このエディタで\n"
	"は, アップグレードの対象となる全てのパーティションのマウント指定\n"
	"を行なう必要があります. 完全に確信の持てる操作でないのであれば,\n"
	"「絶対に」 newfs フラグを Y に設定しないで下さい. この場合には, ラ\n"
	"ベルエディタを画面指向のパーティションのマウント・ツールとして\n"
	"使用することになります. ラベルエディタでの処理が終ったら,\n"
	"Q を押してこの場所に戻り, 次のステップに移りましょう.");
	msgConfirm(KM("OK.  First, we're going to go to the disk label editor.  In this editor\n"
		   "you will be expected to Mount any partitions you're interested in\n"
		   "upgrading.  DO NOT set the Newfs flag to Y on anything in the label editor\n"
		   "unless you're absolutely sure you know what you're doing!  In this\n"
		   "instance, you'll be using the label editor as little more than a fancy\n"
		   "screen-oriented partition mounting tool.\n\n"
		   "Once you're done in the label editor, press Q to return here for the next\n"
		   "step."));

	if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE) {
	    KANJI_MESSAGE("ディスクラベルエディタがエラーステータスを返しました. アップグレー\n"
	    "ド作業は中断します.");
	    msgConfirm(KM("The disk label editor returned an error status.  Upgrade operation\n"
		       "aborted."));
	    return DITEM_FAILURE;
	}

	/* Don't write out MBR info */
	variable_set2(DISK_PARTITIONED, "written", 0);
	if (DITEM_STATUS(diskLabelCommit(self)) == DITEM_FAILURE) {
	    KANJI_MESSAGE("正常にマウントできなかったファイルシステムがあります. アップグレー\n"
	    "ド作業は中断します.");
	    msgConfirm(KM("Not all file systems were properly mounted.  Upgrade operation\n"
		       "aborted."));
	    variable_unset(DISK_PARTITIONED);
	    return DITEM_FAILURE;
	}

	if (extractingBin) {
	    KANJI_MESSAGE("古いバイナリを chflag しています - しばらくお待ち下さい.");
	    msgNotify(KM("chflags'ing old binaries - please wait."));
	    (void)vsystem("chflags -R noschg /mnt/");
	}
	KANJI_MESSAGE("ルートファイルシステム中の /stand を更新しています");
	msgNotify(KM("Updating /stand on root filesystem"));
	(void)vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());

	if (DITEM_STATUS(chroot("/mnt")) == DITEM_FAILURE) {
	    KANJI_MESSAGE("/mnt に chroot できません. 原因としては, ルートパーティションに何\n"
	    "らかの異常があるか, 間違った方法でマウントされているか, などが考え\n"
	    "られます.");
	    msgConfirm(KM("Unable to chroot to /mnt - something is wrong with the\n"
		       "root partition or the way it's mounted if this doesn't work."));
	    variable_unset(DISK_PARTITIONED);
	    return DITEM_FAILURE;
	}
	chdir("/");
	systemCreateHoloshell();
    }

    if (!mediaVerify() || !mediaDevice->init(mediaDevice)) {
	KANJI_MESSAGE("アップグレード: メディアを初期化できません.");
	msgNotify(KM("Upgrade: Couldn't initialize media."));
	return DITEM_FAILURE;
    }

    saved_etc = "/usr/tmp/etc";
    Mkdir(saved_etc);
    KANJI_MESSAGE("/etc ディレクトリを保存しています…");
    msgNotify(KM("Preserving /etc directory.."));
    if (vsystem("tar -cpBf - -C /etc . | tar -xpBf - -C %s", saved_etc)) {
	KANJI_MESSAGE("/etc を %s にバックアップしています.");
	msgNotify(KM("Unable to backup your /etc into %s."), saved_etc);
	return DITEM_FAILURE;
    }

    if (file_readable("/kernel")) {
	KANJI_MESSAGE("古いカーネルを /kernel.prev に名称変更しています");
	msgNotify(KM("Moving old kernel to /kernel.prev"));
	if (!system("chflags noschg /kernel && mv /kernel /kernel.prev")) {
	    /* Give us a working kernel in case we crash and reboot */
	    system("cp /kernel.prev /kernel");
	}
    }

    KANJI_MESSAGE("配布ファイルの展開を開始します…");
    msgNotify(KM("Beginning extraction of distributions.."));
    if (DITEM_STATUS(distExtractAll(self)) == DITEM_FAILURE) {
	KANJI_MESSAGE("おや. bin 配布ファイルを展開できませんでした. このアップグレード作\n"
	"業は失敗したと考えるべきで, 最初からやり直さなければなりません. す\n"
	"みません. ここでシステムはリブートします.");
	msgConfirm(KM("Hmmmm.  We couldn't even extract the bin distribution.  This upgrade\n"
		   "should be considered a failure and started from the beginning, sorry!\n"
		   "The system will reboot now."));
	dialog_clear();
	systemShutdown(1);
    }
    else if (Dists) {
	if (!(Dists & DIST_BIN)) {
	    KANJI_MESSAGE(
	    "アップグレード作業にはいくつかの問題が生じましたが, 終了しました.\n"
	    "しかし, 最も重要なファイルに関しては成功しています. それほど重要では\n"
	    "ないファイルについては失敗しているという点で, このメッセージは警告で\n"
	    "あると考えます.");
	    msgNotify(KM("The extraction process seems to have had some problems, but we got most\n"
		       "of the essentials.  We'll treat this as a warning since it may have been\n"
		       "only non-essential distributions which failed to upgrade."));
	}
	else {
	    KANJI_MESSAGE("おや. bin 配布ファイルを展開できませんでした. このアップグレード作\n"
	    "業は失敗したと考えるべきで, 最初からやり直さなければなりません. す\n"
	    "みません. ここでシステムはリブートします.");
	    msgConfirm(KM("Hmmmm.  We couldn't even extract the bin distribution.  This upgrade\n"
		       "should be considered a failure and started from the beginning, sorry!\n"
		       "The system will reboot now."));
	    dialog_clear();
	    systemShutdown(1);
	}
    }

    KANJI_MESSAGE(
    "アップグレードの最初の段階は正常に終了しました.");
    msgNotify(KM("First stage of upgrade completed successfully."));
    if (vsystem("tar -cpBf - -C %s . | tar --unlink -xpBf - -C /etc", saved_etc)) {
	KANJI_MESSAGE("以前の /etc/%s を復活できません!");
	msgNotify(KM("Unable to resurrect your old /etc!"));
	return DITEM_FAILURE;
    }
    return DITEM_SUCCESS | DITEM_REDRAW;
}
