Browse Source

ktpd: Fix exit on command

Serj Kalichev 1 year ago
parent
commit
dc761a0a33
4 changed files with 45 additions and 27 deletions
  1. 5 4
      klish/ksession/ksession_parse.c
  2. 2 1
      klish/ktp.h
  3. 4 6
      klish/ktp/ktp_session.c
  4. 34 16
      klish/ktp/ktpd_session.c

+ 5 - 4
klish/ksession/ksession_parse.c

@@ -701,10 +701,11 @@ bool_t ksession_exec_locally(ksession_t *session, const kentry_t *entry,
 	// Session status can be changed while parsing because it can execute
 	// nested ksession_exec_locally() to check for PTYPEs, CONDitions etc.
 	// So check for 'done' flag to propagate it.
-	if (ksession_done(session)) {
-		kexec_free(exec);
-		return BOOL_FALSE; // Because action is not completed
-	}
+// NOTE: Don't interrupt single kexec_t. Let's it to complete.
+//	if (ksession_done(session)) {
+//		kexec_free(exec);
+//		return BOOL_FALSE; // Because action is not completed
+//	}
 
 	// Execute kexec and then wait for completion using local Eloop
 	if (!kexec_exec(exec)) {

+ 2 - 1
klish/ktp.h

@@ -24,7 +24,6 @@ typedef enum {
 	KTP_HELP = 'h',
 	KTP_HELP_ACK = 'H',
 	KTP_NOTIFICATION = 'n',
-	KTP_EXIT = 'x',
 	KTP_AUTH = 'a',
 	KTP_AUTH_ACK = 'A',
 	KTP_KEEPALIVE = 'k',
@@ -46,12 +45,14 @@ typedef enum {
 	KTP_STATUS_INCOMPLETED =	(uint32_t)0x00000002,
 	KTP_STATUS_INTERACTIVE =	(uint32_t)0x00000100,
 	KTP_STATUS_DRY_RUN =		(uint32_t)0x00010000,
+	KTP_STATUS_EXIT =		(uint32_t)0x80000000,
 } ktp_status_e;
 
 #define KTP_STATUS_IS_ERROR(status) (status & KTP_STATUS_ERROR)
 #define KTP_STATUS_IS_INCOMPLETED(status) (status & KTP_STATUS_INCOMPLETED)
 #define KTP_STATUS_IS_INTERACTIVE(status) (status & KTP_STATUS_INTERACTIVE)
 #define KTP_STATUS_IS_DRY_RUN(status) (status & KTP_STATUS_DRY_RUN)
+#define KTP_STATUS_IS_EXIT(status) (status & KTP_STATUS_EXIT)
 
 
 #endif // _klish_ktp_h

+ 4 - 6
klish/ktp/ktp_session.c

@@ -337,7 +337,7 @@ static bool_t ktp_session_process_cmd_ack(ktp_session_t *ktp, const faux_msg_t *
 				ktp->cb[KTP_SESSION_CB_CMD_ACK_INCOMPLETED].fn)(
 				ktp, msg,
 				ktp->cb[KTP_SESSION_CB_CMD_ACK_INCOMPLETED].udata);
-
+printf("INCOMPLETED\n");
 		return BOOL_TRUE;
 	}
 
@@ -349,6 +349,7 @@ static bool_t ktp_session_process_cmd_ack(ktp_session_t *ktp, const faux_msg_t *
 		faux_error_add(ktp->error, error_str);
 		faux_str_free(error_str);
 	}
+printf("COMPLETED %d %s\n", ktp->cmd_retcode, error_str);
 	ktp->cmd_retcode_available = BOOL_TRUE; // Answer from server was received
 	ktp->request_done = BOOL_TRUE;
 	ktp->state = KTP_SESSION_STATE_IDLE;
@@ -362,7 +363,7 @@ static bool_t ktp_session_process_cmd_ack(ktp_session_t *ktp, const faux_msg_t *
 	return BOOL_TRUE;
 }
 
-
+/*
 static bool_t ktp_session_process_exit(ktp_session_t *ktp, const faux_msg_t *msg)
 {
 	assert(ktp);
@@ -378,7 +379,7 @@ static bool_t ktp_session_process_exit(ktp_session_t *ktp, const faux_msg_t *msg
 
 	return BOOL_TRUE;
 }
-
+*/
 
 static bool_t ktp_session_dispatch(ktp_session_t *ktp, faux_msg_t *msg)
 {
@@ -415,9 +416,6 @@ static bool_t ktp_session_dispatch(ktp_session_t *ktp, faux_msg_t *msg)
 		}
 		rc = ktp_session_process_stderr(ktp, msg);
 		break;
-	case KTP_EXIT: // Async event
-		rc = ktp_session_process_exit(ktp, msg);
-		break;
 	default:
 		syslog(LOG_WARNING, "Unsupported command: 0x%04u\n", cmd); // Ignore
 		break;

+ 34 - 16
klish/ktp/ktpd_session.c

@@ -41,6 +41,7 @@ struct ktpd_session_s {
 	faux_hdr_t *hdr; // Engine will receive header and then msg
 	faux_eloop_t *eloop; // External link, dont's free()
 	kexec_t *exec;
+	bool_t exit;
 };
 
 
@@ -75,6 +76,13 @@ ktpd_session_t *ktpd_session_new(int sock, kscheme_t *scheme,
 	ktpd->eloop = eloop;
 	ktpd->session = ksession_new(scheme, start_entry);
 	assert(ktpd->session);
+	ktpd->exec = NULL;
+	// Exit flag. It differs from ksession done flag because KTPD session
+	// can't exit immediately. It must finish current command processing
+	// before really stop the event loop. Note: User defined plugin
+	// function must use ksession done flag. This exit flag is internal
+	// feature of KTPD session.
+	ktpd->exit = BOOL_FALSE;
 
 	// Async object
 	ktpd->async = faux_async_new(sock);
@@ -117,6 +125,7 @@ static bool_t ktpd_session_process_cmd(ktpd_session_t *ktpd, faux_msg_t *msg)
 	faux_error_t *error = NULL;
 	bool_t rc = BOOL_FALSE;
 	bool_t dry_run = BOOL_FALSE;
+	uint32_t status = KTP_STATUS_NONE;
 
 	assert(ktpd);
 	assert(msg);
@@ -133,6 +142,7 @@ static bool_t ktpd_session_process_cmd(ktpd_session_t *ktpd, faux_msg_t *msg)
 
 	error = faux_error_new();
 
+	ktpd->exec = NULL;
 	rc = ktpd_session_exec(ktpd, line, &retcode, error, dry_run);
 	faux_str_free(line);
 
@@ -149,16 +159,15 @@ static bool_t ktpd_session_process_cmd(ktpd_session_t *ktpd, faux_msg_t *msg)
 		return BOOL_TRUE; // Continue and wait for ACTION
 	}
 
-	// Session status can be changed while parsing
+	// Here we don't need to wait for the action. We have retcode already.
 	if (ksession_done(ktpd->session)) {
-		ktp_send_error(ktpd->async, cmd, "Interrupted by system");
-		faux_error_free(error);
-		return BOOL_FALSE;
+		ktpd->exit = BOOL_TRUE;
+		status |= KTP_STATUS_EXIT;
 	}
 
 	if (rc) {
 		uint8_t retcode8bit = 0;
-		faux_msg_t *ack = ktp_msg_preform(cmd, KTP_STATUS_NONE);
+		faux_msg_t *ack = ktp_msg_preform(cmd, status);
 		retcode8bit = (uint8_t)(retcode & 0xff);
 		faux_msg_add_param(ack, KTP_PARAM_RETCODE, &retcode8bit, 1);
 		faux_msg_send_async(ack, ktpd->async);
@@ -335,10 +344,6 @@ static bool_t ktpd_session_read_cb(faux_async_t *async,
 	ktpd_session_dispatch(ktpd, completed_msg);
 	faux_msg_free(completed_msg);
 
-	// Session status can be changed while parsing
-	if (ksession_done(ktpd->session))
-		return BOOL_FALSE;
-
 	return BOOL_TRUE;
 }
 
@@ -375,6 +380,7 @@ static bool_t wait_for_actions_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
 	uint8_t retcode8bit = 0;
 	faux_msg_t *ack = NULL;
 	ktp_cmd_e cmd = KTP_CMD_ACK;
+	uint32_t status = KTP_STATUS_NONE;
 
 	if (!ktpd)
 		return BOOL_FALSE;
@@ -399,8 +405,14 @@ static bool_t wait_for_actions_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
 	ktpd->exec = NULL;
 	ktpd->state = KTPD_SESSION_STATE_IDLE;
 
+	// All kexec_t actions are done so can break the loop if needed.
+	if (ksession_done(ktpd->session)) {
+		ktpd->exit = BOOL_TRUE;
+		status |= KTP_STATUS_EXIT; // Notify client about exiting
+	}
+
 	// Send ACK message
-	ack = ktp_msg_preform(cmd, KTP_STATUS_NONE);
+	ack = ktp_msg_preform(cmd, status);
 	retcode8bit = (uint8_t)(retcode & 0xff);
 	faux_msg_add_param(ack, KTP_PARAM_RETCODE, &retcode8bit, 1);
 	faux_msg_send_async(ack, ktpd->async);
@@ -409,6 +421,9 @@ static bool_t wait_for_actions_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
 	type = type; // Happy compiler
 	associated_data = associated_data; // Happy compiler
 
+	if (ktpd->exit)
+		return BOOL_FALSE;
+
 	return BOOL_TRUE;
 }
 
@@ -553,10 +568,11 @@ static bool_t ktpd_session_exec(ktpd_session_t *ktpd, const char *line,
 	kexec_set_dry_run(exec, dry_run);
 
 	// Session status can be changed while parsing
-	if (ksession_done(ktpd->session)) {
-		kexec_free(exec);
-		return BOOL_FALSE; // Because action is not completed
-	}
+// NOTE: kexec_t is atomic now
+//	if (ksession_done(ktpd->session)) {
+//		kexec_free(exec);
+//		return BOOL_FALSE; // Because action is not completed
+//	}
 
 	// Execute kexec and then wait for completion using global Eloop
 	if (!kexec_exec(exec)) {
@@ -636,8 +652,10 @@ bool_t client_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
 
 	type = type; // Happy compiler
 
-	// Session status can be finished here
-	if (ksession_done(ktpd->session))
+	// Session can be really finished here. Note KTPD session can't be
+	// stopped immediately so it's only two places within code to really
+	// break the loop. This one and within wait_for_action_ev().
+	if (ktpd->exit)
 		return BOOL_FALSE;
 
 	return BOOL_TRUE;