// File: /workspace/github_cpp_makefile_star2_O2/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
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
pushq %rax
movq %rdi, %rbx
cmpl $0x5, 0x484(%rdi)
ja 0x239a3
movl $0x6, 0x7c(%rbx)
movb $0x1, %al
jmp 0x23c90
movl %esi, %ebp
leaq 0x80(%rbx), %r14
movl 0x480(%rbx), %eax
leal 0x1(%rax), %ecx
addl $0xfffffc01, %eax       # imm = 0xFFFFFC01
cmpl $0x400, %ecx            # imm = 0x400
cmovbl %ecx, %eax
movb 0x80(%rbx,%rax), %r13b
leaq 0x4(%rsp), %r15
pushq $0x2
popq %rdx
pushq $0x4
popq %rcx
movq %r14, %rdi
movq %r15, %rsi
callq 0x25e18
movl (%r15), %esi
movq %rbx, %rdi
callq 0x26056
testq %rax, %rax
je 0x23a9e
movq %rax, %r12
movb $0x4f, (%rsp)
movl 0x480(%rbx), %eax
leal 0x1(%rax), %ecx
addl $0xfffffc01, %eax       # imm = 0xFFFFFC01
cmpl $0x400, %ecx            # imm = 0x400
cmovbl %ecx, %eax
cmpb $0x0, 0x80(%rbx,%rax)
jns 0x23a33
movq %rsp, %rsi
pushq $0x1
popq %rdx
movq %rbx, %rdi
callq 0x234ec
testb %al, %al
je 0x23c8e
movb $0x32, (%rsp)
movzbl %bpl, %eax
addl $-0x3, %eax
cmpl $0x3, %eax
ja 0x23c8e
leaq 0xc9e2(%rip), %rcx      # 0x30430
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq 0x70(%rbx), %rax
cmpb $0x0, 0x28(%rax)
je 0x23bbb
movzbl 0x2c(%r12), %eax
cmpl $0x3, %eax
je 0x23a74
testl %eax, %eax
jne 0x23a83
testb $0x2, 0x10c(%r12)
jne 0x23bf6
testb $0x1, %r13b
je 0x23a91
movq %r12, %rdi
callq 0x1f3aa
movq %r12, %rdi
callq 0x1e6dc
jmp 0x23c15
leaq 0x3(%rsp), %rsi
movb $0x34, (%rsi)
pushq $0x1
popq %rdx
movq %rbx, %rdi
callq 0x234ec
testb %al, %al
je 0x23c90
movb $0x1, 0x78(%rbx)
jmp 0x23c90
movq 0x70(%rbx), %rax
cmpb $0x0, 0x28(%rax)
je 0x23bbb
movzbl 0x2c(%r12), %eax
cmpl $0x3, %eax
je 0x23adf
testl %eax, %eax
jne 0x23aee
testb $0x2, 0x10c(%r12)
jne 0x23bf6
movb %r13b, 0x1(%rsp)
movq %r12, %r15
addq $0x130, %r15            # imm = 0x130
xorl %ebp, %ebp
movq %r15, %r13
movq (%r13), %r13
cmpq %r15, %r13
je 0x23bfc
movq 0x10(%r13), %rsi
movl 0x14(%rsi), %eax
andl $-0x2, %eax
cmpl $0x4, %eax
je 0x23b02
movq (%rsi), %rdi
movb 0x2c(%rdi), %al
decb %al
cmpb $0x1, %al
ja 0x23b02
movb $0x1, %bpl
cmpb $0x0, 0x11(%rsi)
jne 0x23b02
callq 0x26090
jmp 0x23b02
testb $0x1, %r13b
je 0x23b49
orb $0x2, 0x10c(%r12)
movq %r12, %rdi
xorl %esi, %esi
callq 0x1f12e
movq 0x70(%rbx), %rdi
callq 0xcdd2
cmpb $0x0, 0x2c(%r12)
jne 0x23c68
jmp 0x23c64
movl 0x480(%rbx), %eax
leal 0x1(%rax), %ecx
addl $0xfffffc01, %eax       # imm = 0xFFFFFC01
cmpl $0x400, %ecx            # imm = 0x400
cmovbl %ecx, %eax
movb 0x80(%rbx,%rax), %bpl
testb $0x4, %bpl
je 0x23b9c
movq 0x70(%rbx), %rax
cmpb $0x0, 0x28(%rax)
je 0x23bbb
movb 0x2c(%r12), %al
decb %al
cmpb $0x1, %al
ja 0x23bc4
testb $0xc, 0x10c(%r12)
je 0x23bc4
movb $0x44, (%rsp)
jmp 0x23c68
movb $0x45, (%rsp)
jmp 0x23c68
testb $0x2, %bpl
jne 0x23c28
testb $0x4, %bpl
jne 0x23c45
testb $0x1, %r13b
je 0x23bdf
orb $0x2, 0x10c(%r12)
pushq $0x1
popq %rsi
movq %r12, %rdi
callq 0x1f12e
movq %r12, %rdi
callq 0x1f0d2
xorl %ebp, %ebp
jmp 0x23c54
movb $0x43, (%rsp)
jmp 0x23c68
testb $0x1, %bpl
jne 0x23c06
movb $0x33, (%rsp)
testb $0x1, 0x1(%rsp)
je 0x23c15
movq %r12, %rdi
callq 0x1f3aa
movq 0x70(%rbx), %rdi
callq 0xcdd2
cmpb $0x2, 0x2c(%r12)
je 0x23c64
jmp 0x23c68
leaq 0x2(%rsp), %rdx
movq %rbx, %rdi
movq %r12, %rsi
callq 0x2557a
testb %al, %al
je 0x23c8e
cmpb $0x0, 0x2(%rsp)
je 0x23bca
jmp 0x23c7a
movq %r12, %rdi
callq 0x1f1da
movb $0x2, %bpl
testb %al, %al
je 0x23c9f
movq 0x70(%rbx), %rdi
callq 0xcdd2
cmpb %bpl, 0x2c(%r12)
jne 0x23c68
movb $0x3d, (%rsp)
movq %rsp, %rsi
pushq $0x1
popq %rdx
movq %rbx, %rdi
callq 0x234ec
testb %al, %al
je 0x23c8e
pushq $0x6
popq %rsi
movq %r14, %rdi
callq 0x25df2
andl $0x0, 0x7c(%rbx)
jmp 0x2399c
xorl %eax, %eax
addq $0x8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movb $0x33, (%rsp)
jmp 0x23c68
nop