1 /* 2 * Copyright (c) 2013, Salvatore Sanfilippo <antirez at gmail dot com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * * Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * * Neither the name of Redis nor the names of its contributors may be used 14 * to endorse or promote products derived from this software without 15 * specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include "server.h" 31 32 /* This file implements keyspace events notification via Pub/Sub and 33 * described at https://redis.io/topics/notifications. */ 34 35 /* Turn a string representing notification classes into an integer 36 * representing notification classes flags xored. 37 * 38 * The function returns -1 if the input contains characters not mapping to 39 * any class. */ 40 int keyspaceEventsStringToFlags(char *classes) { 41 char *p = classes; 42 int c, flags = 0; 43 44 while((c = *p++) != '\0') { 45 switch(c) { 46 case 'A': flags |= NOTIFY_ALL; break; 47 case 'g': flags |= NOTIFY_GENERIC; break; 48 case '$': flags |= NOTIFY_STRING; break; 49 case 'l': flags |= NOTIFY_LIST; break; 50 case 's': flags |= NOTIFY_SET; break; 51 case 'h': flags |= NOTIFY_HASH; break; 52 case 'z': flags |= NOTIFY_ZSET; break; 53 case 'x': flags |= NOTIFY_EXPIRED; break; 54 case 'e': flags |= NOTIFY_EVICTED; break; 55 case 'K': flags |= NOTIFY_KEYSPACE; break; 56 case 'E': flags |= NOTIFY_KEYEVENT; break; 57 case 't': flags |= NOTIFY_STREAM; break; 58 default: return -1; 59 } 60 } 61 return flags; 62 } 63 64 /* This function does exactly the revese of the function above: it gets 65 * as input an integer with the xored flags and returns a string representing 66 * the selected classes. The string returned is an sds string that needs to 67 * be released with sdsfree(). */ 68 sds keyspaceEventsFlagsToString(int flags) { 69 sds res; 70 71 res = sdsempty(); 72 if ((flags & NOTIFY_ALL) == NOTIFY_ALL) { 73 res = sdscatlen(res,"A",1); 74 } else { 75 if (flags & NOTIFY_GENERIC) res = sdscatlen(res,"g",1); 76 if (flags & NOTIFY_STRING) res = sdscatlen(res,"$",1); 77 if (flags & NOTIFY_LIST) res = sdscatlen(res,"l",1); 78 if (flags & NOTIFY_SET) res = sdscatlen(res,"s",1); 79 if (flags & NOTIFY_HASH) res = sdscatlen(res,"h",1); 80 if (flags & NOTIFY_ZSET) res = sdscatlen(res,"z",1); 81 if (flags & NOTIFY_EXPIRED) res = sdscatlen(res,"x",1); 82 if (flags & NOTIFY_EVICTED) res = sdscatlen(res,"e",1); 83 if (flags & NOTIFY_STREAM) res = sdscatlen(res,"t",1); 84 } 85 if (flags & NOTIFY_KEYSPACE) res = sdscatlen(res,"K",1); 86 if (flags & NOTIFY_KEYEVENT) res = sdscatlen(res,"E",1); 87 return res; 88 } 89 90 /* The API provided to the rest of the Redis core is a simple function: 91 * 92 * notifyKeyspaceEvent(char *event, robj *key, int dbid); 93 * 94 * 'event' is a C string representing the event name. 95 * 'key' is a Redis object representing the key name. 96 * 'dbid' is the database ID where the key lives. */ 97 void notifyKeyspaceEvent(int type, char *event, robj *key, int dbid) { 98 sds chan; 99 robj *chanobj, *eventobj; 100 int len = -1; 101 char buf[24]; 102 103 /* If any modules are interested in events, notify the module system now. 104 * This bypasses the notifications configuration, but the module engine 105 * will only call event subscribers if the event type matches the types 106 * they are interested in. */ 107 moduleNotifyKeyspaceEvent(type, event, key, dbid); 108 109 /* If notifications for this class of events are off, return ASAP. */ 110 if (!(server.notify_keyspace_events & type)) return; 111 112 eventobj = createStringObject(event,strlen(event)); 113 114 /* __keyspace@<db>__:<key> <event> notifications. */ 115 if (server.notify_keyspace_events & NOTIFY_KEYSPACE) { 116 chan = sdsnewlen("__keyspace@",11); 117 len = ll2string(buf,sizeof(buf),dbid); 118 chan = sdscatlen(chan, buf, len); 119 chan = sdscatlen(chan, "__:", 3); 120 chan = sdscatsds(chan, key->ptr); 121 chanobj = createObject(OBJ_STRING, chan); 122 pubsubPublishMessage(chanobj, eventobj); 123 decrRefCount(chanobj); 124 } 125 126 /* __keyevent@<db>__:<event> <key> notifications. */ 127 if (server.notify_keyspace_events & NOTIFY_KEYEVENT) { 128 chan = sdsnewlen("__keyevent@",11); 129 if (len == -1) len = ll2string(buf,sizeof(buf),dbid); 130 chan = sdscatlen(chan, buf, len); 131 chan = sdscatlen(chan, "__:", 3); 132 chan = sdscatsds(chan, eventobj->ptr); 133 chanobj = createObject(OBJ_STRING, chan); 134 pubsubPublishMessage(chanobj, key); 135 decrRefCount(chanobj); 136 } 137 decrRefCount(eventobj); 138 } 139