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 "redis.h" 31 32 /* This file implements keyspace events notification via Pub/Sub ad 33 * described at http://redis.io/topics/keyspace-events. */ 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 |= REDIS_NOTIFY_ALL; break; 47 case 'g': flags |= REDIS_NOTIFY_GENERIC; break; 48 case '$': flags |= REDIS_NOTIFY_STRING; break; 49 case 'l': flags |= REDIS_NOTIFY_LIST; break; 50 case 's': flags |= REDIS_NOTIFY_SET; break; 51 case 'h': flags |= REDIS_NOTIFY_HASH; break; 52 case 'z': flags |= REDIS_NOTIFY_ZSET; break; 53 case 'x': flags |= REDIS_NOTIFY_EXPIRED; break; 54 case 'e': flags |= REDIS_NOTIFY_EVICTED; break; 55 case 'K': flags |= REDIS_NOTIFY_KEYSPACE; break; 56 case 'E': flags |= REDIS_NOTIFY_KEYEVENT; break; 57 default: return -1; 58 } 59 } 60 return flags; 61 } 62 63 /* This function does exactly the revese of the function above: it gets 64 * as input an integer with the xored flags and returns a string representing 65 * the selected classes. The string returned is an sds string that needs to 66 * be released with sdsfree(). */ 67 sds keyspaceEventsFlagsToString(int flags) { 68 sds res; 69 70 res = sdsempty(); 71 if ((flags & REDIS_NOTIFY_ALL) == REDIS_NOTIFY_ALL) { 72 res = sdscatlen(res,"A",1); 73 } else { 74 if (flags & REDIS_NOTIFY_GENERIC) res = sdscatlen(res,"g",1); 75 if (flags & REDIS_NOTIFY_STRING) res = sdscatlen(res,"$",1); 76 if (flags & REDIS_NOTIFY_LIST) res = sdscatlen(res,"l",1); 77 if (flags & REDIS_NOTIFY_SET) res = sdscatlen(res,"s",1); 78 if (flags & REDIS_NOTIFY_HASH) res = sdscatlen(res,"h",1); 79 if (flags & REDIS_NOTIFY_ZSET) res = sdscatlen(res,"z",1); 80 if (flags & REDIS_NOTIFY_EXPIRED) res = sdscatlen(res,"x",1); 81 if (flags & REDIS_NOTIFY_EVICTED) res = sdscatlen(res,"e",1); 82 } 83 if (flags & REDIS_NOTIFY_KEYSPACE) res = sdscatlen(res,"K",1); 84 if (flags & REDIS_NOTIFY_KEYEVENT) res = sdscatlen(res,"E",1); 85 return res; 86 } 87 88 /* The API provided to the rest of the Redis core is a simple function: 89 * 90 * notifyKeyspaceEvent(char *event, robj *key, int dbid); 91 * 92 * 'event' is a C string representing the event name. 93 * 'key' is a Redis object representing the key name. 94 * 'dbid' is the database ID where the key lives. */ 95 void notifyKeyspaceEvent(int type, char *event, robj *key, int dbid) { 96 sds chan; 97 robj *chanobj, *eventobj; 98 int len = -1; 99 char buf[24]; 100 101 /* If notifications for this class of events are off, return ASAP. */ 102 if (!(server.notify_keyspace_events & type)) return; 103 104 eventobj = createStringObject(event,strlen(event)); 105 106 /* __keyspace@<db>__:<key> <event> notifications. */ 107 if (server.notify_keyspace_events & REDIS_NOTIFY_KEYSPACE) { 108 chan = sdsnewlen("__keyspace@",11); 109 len = ll2string(buf,sizeof(buf),dbid); 110 chan = sdscatlen(chan, buf, len); 111 chan = sdscatlen(chan, "__:", 3); 112 chan = sdscatsds(chan, key->ptr); 113 chanobj = createObject(REDIS_STRING, chan); 114 pubsubPublishMessage(chanobj, eventobj); 115 decrRefCount(chanobj); 116 } 117 118 /* __keyevente@<db>__:<event> <key> notifications. */ 119 if (server.notify_keyspace_events & REDIS_NOTIFY_KEYEVENT) { 120 chan = sdsnewlen("__keyevent@",11); 121 if (len == -1) len = ll2string(buf,sizeof(buf),dbid); 122 chan = sdscatlen(chan, buf, len); 123 chan = sdscatlen(chan, "__:", 3); 124 chan = sdscatsds(chan, eventobj->ptr); 125 chanobj = createObject(REDIS_STRING, chan); 126 pubsubPublishMessage(chanobj, key); 127 decrRefCount(chanobj); 128 } 129 decrRefCount(eventobj); 130 } 131