@@ -3398,6 +3398,34 @@ void call(client *c, int flags) {
33983398 server .stat_numcommands ++ ;
33993399}
34003400
3401+ /* Used when a command that is ready for execution needs to be rejected, due to
3402+ * varios pre-execution checks. it returns the appropriate error to the client.
3403+ * If there's a transaction is flags it as dirty, and if the command is EXEC,
3404+ * it aborts the transaction. */
3405+ void rejectCommand (client * c , robj * reply ) {
3406+ flagTransaction (c );
3407+ if (c -> cmd && c -> cmd -> proc == execCommand ) {
3408+ execCommandAbort (c , reply -> ptr );
3409+ } else {
3410+ /* using addReplyError* rather than addReply so that the error can be logged. */
3411+ addReplyErrorSafe (c , reply -> ptr , sdslen (reply -> ptr ));
3412+ }
3413+ }
3414+
3415+ void rejectCommandFormat (client * c , const char * fmt , ...) {
3416+ flagTransaction (c );
3417+ va_list ap ;
3418+ va_start (ap ,fmt );
3419+ sds s = sdscatvprintf (sdsempty (),fmt ,ap );
3420+ va_end (ap );
3421+ if (c -> cmd && c -> cmd -> proc == execCommand ) {
3422+ execCommandAbort (c , s );
3423+ } else {
3424+ addReplyErrorSafe (c , s , sdslen (s ));
3425+ }
3426+ sdsfree (s );
3427+ }
3428+
34013429/* If this function gets called we already read a whole
34023430 * command, arguments are in the client argv/argc fields.
34033431 * processCommand() execute the command or prepare the
@@ -3423,23 +3451,30 @@ int processCommand(client *c) {
34233451 * such as wrong arity, bad command name and so forth. */
34243452 c -> cmd = c -> lastcmd = lookupCommand (c -> argv [0 ]-> ptr );
34253453 if (!c -> cmd ) {
3426- flagTransaction (c );
34273454 sds args = sdsempty ();
34283455 int i ;
34293456 for (i = 1 ; i < c -> argc && sdslen (args ) < 128 ; i ++ )
34303457 args = sdscatprintf (args , "`%.*s`, " , 128 - (int )sdslen (args ), (char * )c -> argv [i ]-> ptr );
3431- addReplyErrorFormat (c ,"unknown command `%s`, with args beginning with: %s" ,
3458+ rejectCommandFormat (c ,"unknown command `%s`, with args beginning with: %s" ,
34323459 (char * )c -> argv [0 ]-> ptr , args );
34333460 sdsfree (args );
34343461 return C_OK ;
34353462 } else if ((c -> cmd -> arity > 0 && c -> cmd -> arity != c -> argc ) ||
34363463 (c -> argc < - c -> cmd -> arity )) {
3437- flagTransaction (c );
3438- addReplyErrorFormat (c ,"wrong number of arguments for '%s' command" ,
3464+ rejectCommandFormat (c ,"wrong number of arguments for '%s' command" ,
34393465 c -> cmd -> name );
34403466 return C_OK ;
34413467 }
34423468
3469+ int is_write_command = (c -> cmd -> flags & CMD_WRITE ) ||
3470+ (c -> cmd -> proc == execCommand && (c -> mstate .cmd_flags & CMD_WRITE ));
3471+ int is_denyoom_command = (c -> cmd -> flags & CMD_DENYOOM ) ||
3472+ (c -> cmd -> proc == execCommand && (c -> mstate .cmd_flags & CMD_DENYOOM ));
3473+ int is_denystale_command = !(c -> cmd -> flags & CMD_STALE ) ||
3474+ (c -> cmd -> proc == execCommand && (c -> mstate .cmd_inv_flags & CMD_STALE ));
3475+ int is_denyloading_command = !(c -> cmd -> flags & CMD_LOADING ) ||
3476+ (c -> cmd -> proc == execCommand && (c -> mstate .cmd_inv_flags & CMD_LOADING ));
3477+
34433478 /* Check if the user is authenticated. This check is skipped in case
34443479 * the default user is flagged as "nopass" and is active. */
34453480 int auth_required = (!(DefaultUser -> flags & USER_FLAG_NOPASS ) ||
@@ -3449,8 +3484,7 @@ int processCommand(client *c) {
34493484 /* AUTH and HELLO and no auth modules are valid even in
34503485 * non-authenticated state. */
34513486 if (!(c -> cmd -> flags & CMD_NO_AUTH )) {
3452- flagTransaction (c );
3453- addReply (c ,shared .noautherr );
3487+ rejectCommand (c ,shared .noautherr );
34543488 return C_OK ;
34553489 }
34563490 }
@@ -3461,13 +3495,12 @@ int processCommand(client *c) {
34613495 int acl_retval = ACLCheckCommandPerm (c ,& acl_keypos );
34623496 if (acl_retval != ACL_OK ) {
34633497 addACLLogEntry (c ,acl_retval ,acl_keypos ,NULL );
3464- flagTransaction (c );
34653498 if (acl_retval == ACL_DENIED_CMD )
3466- addReplyErrorFormat (c ,
3499+ rejectCommandFormat (c ,
34673500 "-NOPERM this user has no permissions to run "
34683501 "the '%s' command or its subcommand" , c -> cmd -> name );
34693502 else
3470- addReplyErrorFormat (c ,
3503+ rejectCommandFormat (c ,
34713504 "-NOPERM this user has no permissions to access "
34723505 "one of the keys used as arguments" );
34733506 return C_OK ;
@@ -3515,13 +3548,11 @@ int processCommand(client *c) {
35153548 * is trying to execute is denied during OOM conditions or the client
35163549 * is in MULTI/EXEC context? Error. */
35173550 if (out_of_memory &&
3518- (c -> cmd -> flags & CMD_DENYOOM ||
3551+ (is_denyoom_command ||
35193552 (c -> flags & CLIENT_MULTI &&
3520- c -> cmd -> proc != execCommand &&
35213553 c -> cmd -> proc != discardCommand )))
35223554 {
3523- flagTransaction (c );
3524- addReply (c , shared .oomerr );
3555+ rejectCommand (c , shared .oomerr );
35253556 return C_OK ;
35263557 }
35273558
@@ -3542,17 +3573,14 @@ int processCommand(client *c) {
35423573 int deny_write_type = writeCommandsDeniedByDiskError ();
35433574 if (deny_write_type != DISK_ERROR_TYPE_NONE &&
35443575 server .masterhost == NULL &&
3545- (c -> cmd -> flags & CMD_WRITE ||
3546- c -> cmd -> proc == pingCommand ))
3576+ (is_write_command || c -> cmd -> proc == pingCommand ))
35473577 {
3548- flagTransaction (c );
35493578 if (deny_write_type == DISK_ERROR_TYPE_RDB )
3550- addReply (c , shared .bgsaveerr );
3579+ rejectCommand (c , shared .bgsaveerr );
35513580 else
3552- addReplySds (c ,
3553- sdscatprintf (sdsempty (),
3581+ rejectCommandFormat (c ,
35543582 "-MISCONF Errors writing to the AOF file: %s\r\n" ,
3555- strerror (server .aof_last_write_errno ))) ;
3583+ strerror (server .aof_last_write_errno ));
35563584 return C_OK ;
35573585 }
35583586
@@ -3561,22 +3589,20 @@ int processCommand(client *c) {
35613589 if (server .masterhost == NULL &&
35623590 server .repl_min_slaves_to_write &&
35633591 server .repl_min_slaves_max_lag &&
3564- c -> cmd -> flags & CMD_WRITE &&
3592+ is_write_command &&
35653593 server .repl_good_slaves_count < server .repl_min_slaves_to_write )
35663594 {
3567- flagTransaction (c );
3568- addReply (c , shared .noreplicaserr );
3595+ rejectCommand (c , shared .noreplicaserr );
35693596 return C_OK ;
35703597 }
35713598
35723599 /* Don't accept write commands if this is a read only slave. But
35733600 * accept write commands if this is our master. */
35743601 if (server .masterhost && server .repl_slave_ro &&
35753602 !(c -> flags & CLIENT_MASTER ) &&
3576- c -> cmd -> flags & CMD_WRITE )
3603+ is_write_command )
35773604 {
3578- flagTransaction (c );
3579- addReply (c , shared .roslaveerr );
3605+ rejectCommand (c , shared .roslaveerr );
35803606 return C_OK ;
35813607 }
35823608
@@ -3588,7 +3614,7 @@ int processCommand(client *c) {
35883614 c -> cmd -> proc != unsubscribeCommand &&
35893615 c -> cmd -> proc != psubscribeCommand &&
35903616 c -> cmd -> proc != punsubscribeCommand ) {
3591- addReplyErrorFormat (c ,
3617+ rejectCommandFormat (c ,
35923618 "Can't execute '%s': only (P)SUBSCRIBE / "
35933619 "(P)UNSUBSCRIBE / PING / QUIT are allowed in this context" ,
35943620 c -> cmd -> name );
@@ -3600,17 +3626,16 @@ int processCommand(client *c) {
36003626 * link with master. */
36013627 if (server .masterhost && server .repl_state != REPL_STATE_CONNECTED &&
36023628 server .repl_serve_stale_data == 0 &&
3603- !( c -> cmd -> flags & CMD_STALE ) )
3629+ is_denystale_command )
36043630 {
3605- flagTransaction (c );
3606- addReply (c , shared .masterdownerr );
3631+ rejectCommand (c , shared .masterdownerr );
36073632 return C_OK ;
36083633 }
36093634
36103635 /* Loading DB? Return an error if the command has not the
36113636 * CMD_LOADING flag. */
3612- if (server .loading && !( c -> cmd -> flags & CMD_LOADING ) ) {
3613- addReply (c , shared .loadingerr );
3637+ if (server .loading && is_denyloading_command ) {
3638+ rejectCommand (c , shared .loadingerr );
36143639 return C_OK ;
36153640 }
36163641
@@ -3625,7 +3650,6 @@ int processCommand(client *c) {
36253650 c -> cmd -> proc != helloCommand &&
36263651 c -> cmd -> proc != replconfCommand &&
36273652 c -> cmd -> proc != multiCommand &&
3628- c -> cmd -> proc != execCommand &&
36293653 c -> cmd -> proc != discardCommand &&
36303654 c -> cmd -> proc != watchCommand &&
36313655 c -> cmd -> proc != unwatchCommand &&
@@ -3636,8 +3660,7 @@ int processCommand(client *c) {
36363660 c -> argc == 2 &&
36373661 tolower (((char * )c -> argv [1 ]-> ptr )[0 ]) == 'k' ))
36383662 {
3639- flagTransaction (c );
3640- addReply (c , shared .slowscripterr );
3663+ rejectCommand (c , shared .slowscripterr );
36413664 return C_OK ;
36423665 }
36433666
0 commit comments