// File: davmac314[P]dinit/src/control.cc
// Function: control_conn_t::process_start_stop(cp_cmd)
bool control_conn_t::process_start_stop(cp_cmd pktType)
{
    using std::string;
    
    constexpr int pkt_size = 2 + sizeof(handle_t);
    
    if (rbuf.get_length() < pkt_size) {
        chklen = pkt_size;
        return true;
    }
    
    // 1 byte: packet type
    // 1 byte: flags
    //    bit 0: 0 = no pin, 1 = pin
    //    bit 1: 0 = force stop, 1 = not forced ("gentle")
    //    bit 2: 0 = don't restart, 1 = restart after stopping
    //      --- (reserved)
    //    bit 7: 0 = no pre-ack, 1 = issue pre-ack
    // 4 bytes: service handle
    
    bool do_pin = ((rbuf[1] & 1) == 1);
    handle_t handle;
    rbuf.extract((char *) &handle, 2, sizeof(handle));
    
    service_record *service = find_service_for_key(handle);
    if (service == nullptr) {
        // Service handle is bad
        char badreqRep[] = { (char)cp_rply::BADREQ };
        if (!queue_packet(badreqRep, 1)) return false;
        bad_conn_close = true;
        return true;
    }
    else {
        char ack_buf[1] = { (char)cp_rply::PREACK };
        if (rbuf[1] & 128) {
            // Issue PREACK before doing anything that might change service state (and cause a
            // service event info packet to be issued as a result). This allows the client to
            // determine whether the info packets were queued before or after the command was
            // processed.
            if (!queue_packet(ack_buf, 1)) return false;
        }

        ack_buf[0] = (char)cp_rply::ACK;
        
        switch (pktType) {
        case cp_cmd::STARTSERVICE:
            // start service, mark as required
            if (services->is_shutting_down()) {
                ack_buf[0] = (char)cp_rply::SHUTTINGDOWN;
                break;
            }
            if ((service->get_state() == service_state_t::STOPPED
                    || service->get_state() == service_state_t::STOPPING)
                    && service->is_stop_pinned()) {
                ack_buf[0] = (char)cp_rply::PINNEDSTOPPED;
                break;
            }
            if (do_pin) service->pin_start();
            service->start();
            services->process_queues();
            if (service->get_state() == service_state_t::STARTED) ack_buf[0] = (char)cp_rply::ALREADYSS;
            break;
        case cp_cmd::STOPSERVICE:
        {
            // force service to stop
            bool do_restart = ((rbuf[1] & 4) == 4);
            bool gentle = ((rbuf[1] & 2) == 2);
            if (do_restart && services->is_shutting_down()) {
                ack_buf[0] = (char)cp_rply::SHUTTINGDOWN;
                break;
            }
            if ((service->get_state() == service_state_t::STARTED
                    || service->get_state() == service_state_t::STARTING)
                    && service->is_start_pinned()) {
                ack_buf[0] = (char)cp_rply::PINNEDSTARTED;
                break;
            }
            if (gentle) {
                // Check dependents; return appropriate response if any will be affected
                bool has_dependents;
                if (!check_dependents(service, has_dependents)) {
                    return false;
                }
                if (has_dependents) {
                    // Reply packet has already been sent
                    goto clear_out;
                }
            }
            service_state_t wanted_state;
            if (do_restart) {
                if (! service->restart()) {
                    ack_buf[0] = (char)cp_rply::NAK;
                    break;
                }
                wanted_state = service_state_t::STARTED;
            }
            else {
                if (do_pin) service->pin_stop();
                service->stop(true);
                service->forced_stop();
                wanted_state = service_state_t::STOPPED;
            }
            services->process_queues();
            if (service->get_state() == wanted_state) ack_buf[0] = (char)cp_rply::ALREADYSS;
            break;
        }
        case cp_cmd::WAKESERVICE:
        {
            // re-attach a service to its (started) dependents, causing it to start.
            if (services->is_shutting_down()) {
                ack_buf[0] = (char)cp_rply::SHUTTINGDOWN;
                break;
            }
            if ((service->get_state() == service_state_t::STOPPED
                    || service->get_state() == service_state_t::STOPPING)
                    && service->is_stop_pinned()) {
                ack_buf[0] = (char)cp_rply::PINNEDSTOPPED;
                break;
            }
            bool found_dpt = false;
            for (auto dpt : service->get_dependents()) {
                if (dpt->is_only_ordering()) continue;
                auto from = dpt->get_from();
                auto from_state = from->get_state();
                if (from_state == service_state_t::STARTED || from_state == service_state_t::STARTING) {
                    found_dpt = true;
                    if (!dpt->holding_acq) {
                        dpt->get_from()->start_dep(*dpt);
                    }
                }
            }
            if (!found_dpt) {
                ack_buf[0] = (char)cp_rply::NAK;
            }

            if (do_pin) service->pin_start();
            services->process_queues();
            if (service->get_state() == service_state_t::STARTED) ack_buf[0] = (char)cp_rply::ALREADYSS;
            break;
        }
        case cp_cmd::RELEASESERVICE:
            // remove required mark, stop if not required by dependents
            if (do_pin) service->pin_stop();
            service->stop(false);
            services->process_queues();
            if (service->get_state() == service_state_t::STOPPED) ack_buf[0] = (char)cp_rply::ALREADYSS;
            break;
        default:
            // avoid warning for unhandled switch/case values
            return false;
        }
        
        if (!queue_packet(ack_buf, 1)) return false;
    }
    
    clear_out:
    // Clear the packet from the buffer
    rbuf.consume(pkt_size);
    chklen = 0;
    return true;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x70, %rsp
movb %sil, %al
movq %rdi, -0x10(%rbp)
movb %al, -0x11(%rbp)
movq -0x10(%rbp), %rdi
movq %rdi, -0x68(%rbp)
movl $0x6, -0x18(%rbp)
addq $0x80, %rdi
callq 0x5e200
cmpl $0x6, %eax
jae 0x58d26
movq -0x68(%rbp), %rax
movl $0x6, 0x7c(%rax)
movb $0x1, -0x1(%rbp)
jmp 0x591c0
movq -0x68(%rbp), %rdi
addq $0x80, %rdi
movl $0x1, %esi
callq 0x5e160
movq -0x68(%rbp), %rdi
movsbl %al, %eax
andl $0x1, %eax
cmpl $0x1, %eax
sete %al
andb $0x1, %al
movb %al, -0x19(%rbp)
addq $0x80, %rdi
leaq -0x20(%rbp), %rsi
movl $0x2, %edx
movl $0x4, %ecx
callq 0x5e220
movq -0x68(%rbp), %rdi
movl -0x20(%rbp), %esi
callq 0x5e970
movq %rax, -0x28(%rbp)
cmpq $0x0, -0x28(%rbp)
jne 0x58dba
movq -0x68(%rbp), %rdi
movb 0x206b4(%rip), %al      # 0x7943f
movb %al, -0x29(%rbp)
leaq -0x29(%rbp), %rsi
movl $0x1, %edx
callq 0x58340
testb $0x1, %al
jne 0x58da9
movb $0x0, -0x1(%rbp)
jmp 0x591c0
movq -0x68(%rbp), %rax
movb $0x1, 0x78(%rax)
movb $0x1, -0x1(%rbp)
jmp 0x591c0
movq -0x68(%rbp), %rdi
movb 0x2067c(%rip), %al      # 0x79440
movb %al, -0x2a(%rbp)
addq $0x80, %rdi
movl $0x1, %esi
callq 0x5e160
movsbl %al, %eax
andl $0x80, %eax
cmpl $0x0, %eax
je 0x58e06
movq -0x68(%rbp), %rdi
leaq -0x2a(%rbp), %rsi
movl $0x1, %edx
callq 0x58340
testb $0x1, %al
jne 0x58e04
movb $0x0, -0x1(%rbp)
jmp 0x591c0
jmp 0x58e06
movb $0x32, -0x2a(%rbp)
movb -0x11(%rbp), %al
addb $-0x3, %al
movzbl %al, %ecx
movq %rcx, -0x70(%rbp)
subb $0x3, %al
ja 0x59176
movq -0x70(%rbp), %rax
leaq 0x205c7(%rip), %rcx     # 0x793f0
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq -0x68(%rbp), %rax
movq 0x70(%rax), %rdi
callq 0x4e280
testb $0x1, %al
jne 0x58e45
jmp 0x58e4e
movb $0x45, -0x2a(%rbp)
jmp 0x5917c
movq -0x28(%rbp), %rdi
callq 0x2f0d0
cmpb $0x0, %al
je 0x58e68
movq -0x28(%rbp), %rdi
callq 0x2f0d0
cmpb $0x3, %al
jne 0x58e80
movq -0x28(%rbp), %rdi
callq 0x5ea00
testb $0x1, %al
jne 0x58e77
jmp 0x58e80
movb $0x43, -0x2a(%rbp)
jmp 0x5917c
testb $0x1, -0x19(%rbp)
je 0x58e8f
movq -0x28(%rbp), %rdi
callq 0x4dbd0
movq -0x28(%rbp), %rdi
callq 0x4c2b0
movq -0x68(%rbp), %rax
movq 0x70(%rax), %rdi
callq 0x173c0
movq -0x28(%rbp), %rdi
callq 0x2f0d0
cmpb $0x2, %al
jne 0x58eb6
movb $0x3d, -0x2a(%rbp)
jmp 0x5917c
movq -0x68(%rbp), %rdi
addq $0x80, %rdi
movl $0x1, %esi
callq 0x5e160
movq -0x68(%rbp), %rdi
movsbl %al, %eax
andl $0x4, %eax
cmpl $0x4, %eax
sete %al
andb $0x1, %al
movb %al, -0x2b(%rbp)
addq $0x80, %rdi
movl $0x1, %esi
callq 0x5e160
movsbl %al, %eax
andl $0x2, %eax
cmpl $0x2, %eax
sete %al
andb $0x1, %al
movb %al, -0x2c(%rbp)
testb $0x1, -0x2b(%rbp)
je 0x58f29
movq -0x68(%rbp), %rax
movq 0x70(%rax), %rdi
callq 0x4e280
testb $0x1, %al
jne 0x58f20
jmp 0x58f29
movb $0x45, -0x2a(%rbp)
jmp 0x5917c
movq -0x28(%rbp), %rdi
callq 0x2f0d0
cmpb $0x2, %al
je 0x58f43
movq -0x28(%rbp), %rdi
callq 0x2f0d0
cmpb $0x1, %al
jne 0x58f5b
movq -0x28(%rbp), %rdi
callq 0x4e360
testb $0x1, %al
jne 0x58f52
jmp 0x58f5b
movb $0x44, -0x2a(%rbp)
jmp 0x5917c
testb $0x1, -0x2c(%rbp)
je 0x58f8c
movq -0x68(%rbp), %rdi
movq -0x28(%rbp), %rsi
leaq -0x2d(%rbp), %rdx
callq 0x5c070
testb $0x1, %al
jne 0x58f7f
movb $0x0, -0x1(%rbp)
jmp 0x591c0
testb $0x1, -0x2d(%rbp)
je 0x58f8a
jmp 0x5919c
jmp 0x58f8c
testb $0x1, -0x2b(%rbp)
je 0x58fae
movq -0x28(%rbp), %rdi
callq 0x4d8f0
testb $0x1, %al
jne 0x58fa8
movb $0x33, -0x2a(%rbp)
jmp 0x5917c
movb $0x2, -0x2e(%rbp)
jmp 0x58fd8
testb $0x1, -0x19(%rbp)
je 0x58fbd
movq -0x28(%rbp), %rdi
callq 0x5ea20
movq -0x28(%rbp), %rdi
movl $0x1, %esi
callq 0x4d7d0
movq -0x28(%rbp), %rdi
callq 0x4d750
movb $0x0, -0x2e(%rbp)
movq -0x68(%rbp), %rax
movq 0x70(%rax), %rdi
callq 0x173c0
movq -0x28(%rbp), %rdi
callq 0x2f0d0
cmpb -0x2e(%rbp), %al
jne 0x58ff7
movb $0x3d, -0x2a(%rbp)
jmp 0x5917c
movq -0x68(%rbp), %rax
movq 0x70(%rax), %rdi
callq 0x4e280
testb $0x1, %al
jne 0x5900f
jmp 0x59018
movb $0x45, -0x2a(%rbp)
jmp 0x5917c
movq -0x28(%rbp), %rdi
callq 0x2f0d0
cmpb $0x0, %al
je 0x59032
movq -0x28(%rbp), %rdi
callq 0x2f0d0
cmpb $0x3, %al
jne 0x5904a
movq -0x28(%rbp), %rdi
callq 0x5ea00
testb $0x1, %al
jne 0x59041
jmp 0x5904a
movb $0x43, -0x2a(%rbp)
jmp 0x5917c
movb $0x0, -0x2f(%rbp)
movq -0x28(%rbp), %rdi
callq 0x30220
movq %rax, -0x38(%rbp)
movq -0x38(%rbp), %rdi
callq 0x304d0
movq %rax, -0x40(%rbp)
movq -0x38(%rbp), %rdi
callq 0x30530
movq %rax, -0x48(%rbp)
leaq -0x40(%rbp), %rdi
leaq -0x48(%rbp), %rsi
callq 0x30500
testb $0x1, %al
jne 0x59088
jmp 0x59103
leaq -0x40(%rbp), %rdi
callq 0x30590
movq (%rax), %rax
movq %rax, -0x50(%rbp)
movq -0x50(%rbp), %rdi
callq 0x4e3f0
testb $0x1, %al
jne 0x590a7
jmp 0x590a9
jmp 0x590f5
movq -0x50(%rbp), %rdi
callq 0x30420
movq %rax, -0x58(%rbp)
movq -0x58(%rbp), %rdi
callq 0x2f0d0
movb %al, -0x59(%rbp)
cmpb $0x2, -0x59(%rbp)
je 0x590ce
cmpb $0x1, -0x59(%rbp)
jne 0x590f3
movb $0x1, -0x2f(%rbp)
movq -0x50(%rbp), %rax
testb $0x1, 0x11(%rax)
jne 0x590f1
movq -0x50(%rbp), %rdi
callq 0x30420
movq %rax, %rdi
movq -0x50(%rbp), %rsi
callq 0x5ea40
jmp 0x590f3
jmp 0x590f5
leaq -0x40(%rbp), %rdi
callq 0x306a0
jmp 0x59075
testb $0x1, -0x2f(%rbp)
jne 0x5910d
movb $0x33, -0x2a(%rbp)
testb $0x1, -0x19(%rbp)
je 0x5911c
movq -0x28(%rbp), %rdi
callq 0x4dbd0
movq -0x68(%rbp), %rax
movq 0x70(%rax), %rdi
callq 0x173c0
movq -0x28(%rbp), %rdi
callq 0x2f0d0
cmpb $0x2, %al
jne 0x5913a
movb $0x3d, -0x2a(%rbp)
jmp 0x5917c
testb $0x1, -0x19(%rbp)
je 0x5914b
movq -0x28(%rbp), %rdi
callq 0x5ea20
movq -0x28(%rbp), %rdi
xorl %esi, %esi
callq 0x4d7d0
movq -0x68(%rbp), %rax
movq 0x70(%rax), %rdi
callq 0x173c0
movq -0x28(%rbp), %rdi
callq 0x2f0d0
cmpb $0x0, %al
jne 0x59174
movb $0x3d, -0x2a(%rbp)
jmp 0x5917c
movb $0x0, -0x1(%rbp)
jmp 0x591c0
movq -0x68(%rbp), %rdi
leaq -0x2a(%rbp), %rsi
movl $0x1, %edx
callq 0x58340
testb $0x1, %al
jne 0x59198
movb $0x0, -0x1(%rbp)
jmp 0x591c0
jmp 0x5919a
jmp 0x5919c
movq -0x68(%rbp), %rdi
addq $0x80, %rdi
movl $0x6, %esi
callq 0x5e1a0
movq -0x68(%rbp), %rax
movl $0x0, 0x7c(%rax)
movb $0x1, -0x1(%rbp)
movb -0x1(%rbp), %al
andb $0x1, %al
addq $0x70, %rsp
popq %rbp
retq
nopl (%rax,%rax)