// File: davmac314[P]dinit/src/shutdown.cc
// Function: do_system_shutdown(shutdown_type_t)
void do_system_shutdown(shutdown_type_t shutdown_type)
{
    using namespace std;
    
    // Mask all signals to prevent death of our parent etc from terminating us
    sigset_t allsigs;
    sigfillset(&allsigs);
    sigprocmask(SIG_SETMASK, &allsigs, nullptr);
    
    int reboot_type = RB_AUTOBOOT; // reboot
    const char *shutdown_type_arg = "reboot";
#if defined(RB_POWER_OFF)
    if (shutdown_type == shutdown_type_t::POWEROFF) {
        reboot_type = RB_POWER_OFF;
        shutdown_type_arg = "poweroff";
    }
#elif defined(RB_POWEROFF)
    // FreeBSD spells it slightly differently
    if (shutdown_type == shutdown_type_t::POWEROFF) {
        reboot_type = RB_POWEROFF;
        shutdown_type_arg = "poweroff";
    }
#elif defined(RB_POWERDOWN)
    // NetBSD (at least) uses RB_POWERDOWN rather than RB_POWER_OFF
    if (shutdown_type == shutdown_type_t::POWEROFF) {
        reboot_type = RB_POWERDOWN;
        shutdown_type_arg = "poweroff";
    }
#endif
#if defined(RB_HALT_SYSTEM)
    // Linux
    if (shutdown_type == shutdown_type_t::HALT) {
        reboot_type = RB_HALT_SYSTEM;
        shutdown_type_arg = "halt";
    }
#elif defined(RB_HALT)
    // NetBSD, FreeBSD
    if (shutdown_type == shutdown_type_t::HALT) {
        reboot_type = RB_HALT;
        shutdown_type_arg = "halt";
    }
#endif
#if defined(RB_KEXEC)
    if (shutdown_type == shutdown_type_t::KEXEC) reboot_type = RB_KEXEC;
#endif
    
    // Write to console rather than any terminal, since we lose the terminal it seems:
    int consfd = open("/dev/console", O_WRONLY);
    if (consfd != STDOUT_FILENO && consfd != -1) {
        dup2(consfd, STDOUT_FILENO);
    }
    
    loop_t loop;
    subproc_buffer sub_buf {loop, STDOUT_FILENO};

    sub_buf.append("Sending TERM/KILL to all processes...\n");
    
    // Send TERM/KILL to all (remaining) processes
    kill(-1, SIGTERM);

    // 1 second delay (while outputting from sub_buf):
    bool timeout_reached = false;
    dasynq::time_val timeout {1, 0};
    dasynq::time_val interval {0,0};
    loop_t::timer::add_timer(loop, clock_type::MONOTONIC, true /* relative */,
            timeout.get_timespec(), interval.get_timespec(),
            [&](loop_t &eloop, int expiry_count) -> rearm {

        timeout_reached = true;
        return rearm::REMOVE;
    });

    do {
      loop.run();
    } while (! timeout_reached);

    kill(-1, SIGKILL);

    // Attempt to execute shutdown hook at three possible locations:
    const char * const hook_paths[] = {
            "/etc/dinit/shutdown-hook",
            "/lib/dinit/shutdown-hook"
    };
    
    bool do_unmount_ourself = true;
    const int execmask = S_IXOTH | S_IXGRP | S_IXUSR;
    struct stat statbuf;

    for (size_t i = 0; i < sizeof(hook_paths) / sizeof(hook_paths[0]); ++i) {
        int stat_r = lstat(hook_paths[i], &statbuf);
        if (stat_r == 0 && (statbuf.st_mode & execmask) != 0) {
            sub_buf.append("Executing shutdown hook...\n");
            const char *prog_args[] = { hook_paths[i], shutdown_type_arg, nullptr };
            try {
                auto r = run_process(prog_args, loop, sub_buf);
                if (r.did_exit() && r.get_exit_status() == 0) {
                    do_unmount_ourself = false;
                }
            }
            catch (std::exception &e) {
                sub_buf.append("Couldn't fork for shutdown-hook: ");
                sub_buf.append(e.what());
                sub_buf.append("\n");
            }
            break;
        }
    }

    // perform shutdown
    if (do_unmount_ourself) {
        sub_buf.append("Turning off swap...\n");
        swap_off(loop, sub_buf);
        sub_buf.append("Unmounting disks...\n");
        unmount_disks(loop, sub_buf);
    }

    sync();
    
    sub_buf.append("Issuing shutdown via kernel...\n");
    loop.poll();  // give message a chance to get to console
#ifdef __NetBSD__
    reboot(reboot_type, NULL);
#else
    if (reboot(reboot_type)) {
        // we're in trouble now
        sub_buf.append("reboot: ");
        sub_buf.append(strerror(errno));
        if (shutdown_type == shutdown_type_t::KEXEC) {
            sub_buf.append(
                    "\nIt is possible that no suitable kernel image was loaded before"
                    "\nreboot with kexec was attempted.\n"
            );
        }
        while (true) loop.run();
    }
#endif
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x1380, %rsp           # imm = 0x1380
movb %dil, %al
movb %al, -0x1(%rbp)
leaq -0x88(%rbp), %rdi
callq 0x3080
movl $0x2, %edi
leaq -0x88(%rbp), %rsi
xorl %eax, %eax
movl %eax, %edx
callq 0x30f0
movl $0x1234567, -0x8c(%rbp) # imm = 0x1234567
leaq 0xda78(%rip), %rax      # 0x1176b
movq %rax, -0x98(%rbp)
cmpb $0x3, -0x1(%rbp)
jne 0x3d18
movl $0x4321fedc, -0x8c(%rbp) # imm = 0x4321FEDC
leaq 0xda61(%rip), %rax      # 0x11772
movq %rax, -0x98(%rbp)
cmpb $0x2, -0x1(%rbp)
jne 0x3d36
movl $0xcdef0123, -0x8c(%rbp) # imm = 0xCDEF0123
leaq 0xda4c(%rip), %rax      # 0x1177b
movq %rax, -0x98(%rbp)
cmpb $0x6, -0x1(%rbp)
jne 0x3d46
movl $0x45584543, -0x8c(%rbp) # imm = 0x45584543
leaq 0xda33(%rip), %rdi      # 0x11780
movl $0x1, %esi
movb $0x0, %al
callq 0x31a0
movl %eax, -0x9c(%rbp)
cmpl $0x1, -0x9c(%rbp)
je 0x3d81
cmpl $-0x1, -0x9c(%rbp)
je 0x3d81
movl -0x9c(%rbp), %edi
movl $0x1, %esi
callq 0x3240
leaq -0x208(%rbp), %rdi
movq %rdi, -0x1358(%rbp)
callq 0x52c0
movq -0x1358(%rbp), %rsi
leaq -0x1230(%rbp), %rdi
movl $0x1, %edx
callq 0x52f0
jmp 0x3dae
leaq 0xd9d8(%rip), %rsi      # 0x1178d
leaq -0x1230(%rbp), %rdi
callq 0x5370
jmp 0x3dc3
movl $0xffffffff, %edi       # imm = 0xFFFFFFFF
movl $0xf, %esi
callq 0x3370
movb $0x0, -0x123d(%rbp)
xorl %eax, %eax
movl %eax, %edx
movq %rdx, -0x1378(%rbp)
leaq -0x1250(%rbp), %rdi
movq %rdi, -0x1370(%rbp)
movl $0x1, %esi
callq 0x53f0
movq -0x1378(%rbp), %rdx
leaq -0x1260(%rbp), %rdi
movq %rdi, -0x1368(%rbp)
movq %rdx, %rsi
callq 0x53f0
movq -0x1370(%rbp), %rdi
callq 0x5420
movq -0x1368(%rbp), %rdi
movq %rax, -0x1360(%rbp)
callq 0x5420
movq -0x1360(%rbp), %rcx
movq %rax, %r8
leaq -0x123d(%rbp), %rax
movq %rax, -0x1268(%rbp)
movq -0x1268(%rbp), %r9
leaq -0x208(%rbp), %rdi
movl $0x1, %edx
movl %edx, %esi
callq 0x4220
jmp 0x3e6c
jmp 0x3e6e
leaq -0x208(%rbp), %rdi
movl $0xffffffff, %esi       # imm = 0xFFFFFFFF
callq 0x5430
movb -0x123d(%rbp), %al
xorb $-0x1, %al
testb $0x1, %al
jne 0x3e6e
movl $0xffffffff, %edi       # imm = 0xFFFFFFFF
movl $0x9, %esi
callq 0x3370
movq 0x1599f(%rip), %rax     # 0x19840
movq %rax, -0x1280(%rbp)
movq 0x15999(%rip), %rax     # 0x19848
movq %rax, -0x1278(%rbp)
movb $0x1, -0x1281(%rbp)
movl $0x49, -0x1288(%rbp)
movq $0x0, -0x1320(%rbp)
cmpq $0x2, -0x1320(%rbp)
jae 0x40c0
movq -0x1320(%rbp), %rax
movq -0x1280(%rbp,%rax,8), %rdi
leaq -0x1318(%rbp), %rsi
callq 0x3430
movl %eax, -0x1324(%rbp)
cmpl $0x0, -0x1324(%rbp)
jne 0x40a7
movl -0x1300(%rbp), %eax
andl $0x49, %eax
cmpl $0x0, %eax
je 0x40a7
leaq 0xd8bf(%rip), %rsi      # 0x117e6
leaq -0x1230(%rbp), %rdi
callq 0x5370
jmp 0x3f35
movq -0x1320(%rbp), %rax
movq -0x1280(%rbp,%rax,8), %rax
movq %rax, -0x1340(%rbp)
movq -0x98(%rbp), %rax
movq %rax, -0x1338(%rbp)
movq $0x0, -0x1330(%rbp)
leaq -0x1340(%rbp), %rdi
leaq -0x208(%rbp), %rsi
leaq -0x1230(%rbp), %rdx
callq 0x42f0
movq %rax, -0x1380(%rbp)
jmp 0x3f87
movq -0x1380(%rbp), %rax
movq %rax, -0x1348(%rbp)
leaq -0x1348(%rbp), %rdi
callq 0x54c0
testb $0x1, %al
jne 0x3faa
jmp 0x4087
leaq -0x1348(%rbp), %rdi
callq 0x54e0
cmpl $0x0, %eax
jne 0x4087
movb $0x0, -0x1281(%rbp)
jmp 0x4087
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x1238(%rbp)
movl %eax, -0x123c(%rbp)
jmp 0x41ed
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x1238(%rbp)
movl %eax, -0x123c(%rbp)
jmp 0x41e1
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x1238(%rbp)
movl %eax, -0x123c(%rbp)
movl -0x123c(%rbp), %eax
movl $0x1, %ecx
cmpl %ecx, %eax
jne 0x41e1
movq -0x1238(%rbp), %rdi
callq 0x30d0
movq %rax, -0x1350(%rbp)
leaq 0xd7ca(%rip), %rsi      # 0x11802
leaq -0x1230(%rbp), %rdi
callq 0x5370
jmp 0x4046
movq -0x1350(%rbp), %rdi
movq (%rdi), %rax
movq 0x10(%rax), %rax
callq *%rax
movq %rax, %rsi
leaq -0x1230(%rbp), %rdi
callq 0x5370
jmp 0x4067
leaq 0xd792(%rip), %rsi      # 0x11800
leaq -0x1230(%rbp), %rdi
callq 0x5370
jmp 0x407c
callq 0x33e0
jmp 0x4083
jmp 0x4085
jmp 0x40c0
jmp 0x4085
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x1238(%rbp)
movl %eax, -0x123c(%rbp)
callq 0x33e0
jmp 0x40a2
jmp 0x41e1
jmp 0x40a9
movq -0x1320(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x1320(%rbp)
jmp 0x3ed2
testb $0x1, -0x1281(%rbp)
je 0x411f
leaq 0xd754(%rip), %rsi      # 0x11824
leaq -0x1230(%rbp), %rdi
callq 0x5370
jmp 0x40de
leaq -0x208(%rbp), %rdi
leaq -0x1230(%rbp), %rsi
callq 0x4580
jmp 0x40f3
leaq 0xd73f(%rip), %rsi      # 0x11839
leaq -0x1230(%rbp), %rdi
callq 0x5370
jmp 0x4108
leaq -0x208(%rbp), %rdi
leaq -0x1230(%rbp), %rsi
callq 0x4670
jmp 0x411d
jmp 0x411f
callq 0x3260
leaq 0xd723(%rip), %rsi      # 0x1184e
leaq -0x1230(%rbp), %rdi
callq 0x5370
jmp 0x4139
leaq -0x208(%rbp), %rdi
movl $0xffffffff, %esi       # imm = 0xFFFFFFFF
callq 0x5500
movl -0x8c(%rbp), %edi
callq 0x3200
cmpl $0x0, %eax
je 0x41c0
leaq 0xd70d(%rip), %rsi      # 0x1186e
leaq -0x1230(%rbp), %rdi
callq 0x5370
jmp 0x416f
callq 0x3030
movl (%rax), %edi
callq 0x30a0
movq %rax, %rsi
leaq -0x1230(%rbp), %rdi
callq 0x5370
jmp 0x418c
cmpb $0x6, -0x1(%rbp)
jne 0x41a9
leaq 0xd6de(%rip), %rsi      # 0x11877
leaq -0x1230(%rbp), %rdi
callq 0x5370
jmp 0x41a7
jmp 0x41a9
jmp 0x41ab
jmp 0x41ad
leaq -0x208(%rbp), %rdi
movl $0xffffffff, %esi       # imm = 0xFFFFFFFF
callq 0x5430
jmp 0x41ab
leaq -0x1230(%rbp), %rdi
callq 0x5560
leaq -0x208(%rbp), %rdi
callq 0x5590
addq $0x1380, %rsp           # imm = 0x1380
popq %rbp
retq
leaq -0x1230(%rbp), %rdi
callq 0x5560
leaq -0x208(%rbp), %rdi
callq 0x5590
movq -0x1238(%rbp), %rdi
callq 0x3410
movq %rax, %rdi
callq 0x52b0
nopl (%rax)