1 #include <uwsgi.h>
2 
3 extern struct uwsgi_server uwsgi;
4 
5 struct uwsgi_redislog_state {
6 	int fd;
7 	char *password;
8 	char *address;
9 	char *id;
10 	char *command;
11 	char *prefix;
12 	char msgsize[11];
13 	struct iovec iovec[7];
14 	char response[8];
15 };
16 
uwsgi_redis_logger_build_command(char * src)17 static char *uwsgi_redis_logger_build_command(char *src) {
18 	ssize_t len = 4096;
19 	char *dst = uwsgi_calloc(len);
20 	char *orig_dst = dst;
21 	int count = 2;
22 	char *ptr = src;
23 
24 	// first of all, count the number of spaces
25 	while(*ptr++) { if (*ptr == ' ') count++; }
26 
27 	int pos = snprintf(dst, 4096, "*%d\r\n", count);
28 	dst+=pos;
29 	len -= pos;
30 
31 	ptr = src;
32 	char *base = src;
33 
34 	while(*ptr++) {
35 		if (*ptr == ' ') {
36 			pos = snprintf(dst, len, "$%d\r\n%.*s\r\n", (int) (ptr-base), (int) (ptr-base), base);
37 			if (pos >= len || pos < 0) {
38 				// i do not know what to do, better to exit...
39 				exit(1);
40 			}
41 			base = ptr+1;
42 			dst+=pos;
43 			len-= pos;
44 		}
45 	}
46 
47 	pos = snprintf(dst, len, "$%d\r\n%.*s\r\n", (int) ((ptr-1)-base), (int) ((ptr-1)-base), base);
48 	if (pos > len || pos < 0) {
49 		// i do not know what to do, better to exit...
50 		exit(1);
51 	}
52 	return orig_dst;
53 }
54 
uwsgi_redis_logger_discard_response(struct uwsgi_redislog_state * uredislog)55 static ssize_t uwsgi_redis_logger_discard_response(struct uwsgi_redislog_state *uredislog) {
56 	ssize_t ret = 0, ret2;
57 again:
58 	// read til a \n is found (ugly but fast)
59 	ret2 = read(uredislog->fd, uredislog->response, 8);
60 	if (ret2 <= 0) {
61 		close(uredislog->fd);
62 		uredislog->fd = -1;
63 		return -1;
64 	}
65 	ret += ret2;
66 	if (!memchr(uredislog->response, '\n', ret2)) {
67 		goto again;
68 	}
69 	return ret;
70 }
71 
uwsgi_redis_logger(struct uwsgi_logger * ul,char * message,size_t len)72 ssize_t uwsgi_redis_logger(struct uwsgi_logger *ul, char *message, size_t len) {
73 
74 	ssize_t ret;
75 	struct uwsgi_redislog_state *uredislog = NULL;
76 
77 	if (!ul->configured) {
78 
79 		if (!ul->data) {
80 			ul->data = uwsgi_calloc(sizeof(struct uwsgi_redislog_state));
81 			uredislog = (struct uwsgi_redislog_state *) ul->data;
82 		}
83 
84 		if (ul->arg != NULL) {
85 			char *logarg = uwsgi_str(ul->arg);
86 			char *at = strchr(logarg, '@');
87 			if (at) {
88 			  *at = 0;
89 			  uredislog->password = logarg;
90 			  logarg = at + 1;
91 			}
92 			char *comma1 = strchr(logarg, ',');
93 			if (!comma1) {
94 				char *slash = strchr(logarg, '/');
95 				if (slash) {
96 				  *slash = 0;
97 				  uredislog->id = slash + 1;
98 				}
99 				uredislog->address = uwsgi_resolve_ip(logarg);
100 				goto done;
101 			}
102 			*comma1 = 0;
103 			char *slash = strchr(logarg, '/');
104 			if (slash) {
105 			  *slash = 0;
106 			  uredislog->id = slash + 1;
107 			}
108 			uredislog->address = logarg;
109 			comma1++;
110 			if (*comma1 == 0) goto done;
111 
112 			char *comma2 = strchr(comma1,',');
113 			if (!comma2) {
114 				uredislog->command = uwsgi_redis_logger_build_command(comma1);
115 				goto done;
116 			}
117 
118 			*comma2 = 0;
119 			uredislog->command = uwsgi_redis_logger_build_command(comma1);
120 			comma2++;
121 			if (*comma2 == 0) goto done;
122 
123 			uredislog->prefix = comma2;
124 		}
125 
126 done:
127 
128 		if (!uredislog->password) uredislog->password = NULL;
129 		if (!uredislog->id) uredislog->id = "0";
130 		if (!uredislog->address) uredislog->address = uwsgi_str("127.0.0.1:6379");
131 		if (!uredislog->command) uredislog->command = "*3\r\n$7\r\npublish\r\n$5\r\nuwsgi\r\n";
132 		if (!uredislog->prefix) uredislog->prefix = "";
133 
134 		uredislog->fd = -1;
135 
136 		uredislog->iovec[0].iov_base = uredislog->command;
137 		uredislog->iovec[0].iov_len = strlen(uredislog->command);
138 		uredislog->iovec[1].iov_base = "$";
139 		uredislog->iovec[1].iov_len = 1;
140 
141 		uredislog->iovec[2].iov_base = uredislog->msgsize;
142 
143 		uredislog->iovec[3].iov_base = "\r\n";
144 		uredislog->iovec[3].iov_len = 2;
145 
146 		uredislog->iovec[4].iov_base = uredislog->prefix;
147 		uredislog->iovec[4].iov_len = strlen(uredislog->prefix);
148 
149 		uredislog->iovec[6].iov_base = "\r\n";
150 		uredislog->iovec[6].iov_len = 2;
151 
152 		ul->configured = 1;
153 	}
154 
155 	uredislog = (struct uwsgi_redislog_state *) ul->data;
156 	if (uredislog->fd == -1) {
157 		struct iovec	setup_iov;
158 		char		setup_buf[4096];
159 		uredislog->fd = uwsgi_connect(uredislog->address, uwsgi.socket_timeout, 0);
160 		if (uredislog->password) {
161 		  setup_iov.iov_len = snprintf(
162 		    setup_buf, sizeof (setup_buf), "*2\r\n$4\r\nauth\r\n$%zu\r\n%*s\r\n",
163 		    strlen(uredislog->password), (int)strlen(uredislog->password), uredislog->password);
164 		  setup_iov.iov_base = setup_buf;
165 		  ret = writev(uredislog->fd, &setup_iov, 1);
166 		  if (ret <= 0) {
167 		    close(uredislog->fd);
168 		    uredislog->fd = -1;
169 		    return -1;
170 		  }
171 		  uwsgi_redis_logger_discard_response(uredislog);
172 		}
173 		if (uredislog->id) {
174 		  setup_iov.iov_len = snprintf(
175 		    setup_buf, sizeof (setup_buf), "*2\r\n$6\r\nselect\r\n$%zu\r\n%*s\r\n",
176 	            strlen(uredislog->id), (int)strlen(uredislog->id), uredislog->id);
177 		  setup_iov.iov_base = setup_buf;
178 		  ret = writev(uredislog->fd, &setup_iov, 1);
179 		  if (ret <= 0) {
180 		    close(uredislog->fd);
181 		    uredislog->fd = -1;
182 		    return -1;
183 		  }
184 		  uwsgi_redis_logger_discard_response(uredislog);
185 		}
186 	}
187 
188 	if (uredislog->fd == -1) return -1;
189 
190 	// drop newline
191         if (message[len-1] == '\n') len--;
192 
193 	uwsgi_num2str2(len + uredislog->iovec[4].iov_len, uredislog->msgsize);
194 	uredislog->iovec[2].iov_len = strlen(uredislog->msgsize);
195 
196 	uredislog->iovec[5].iov_base = message;
197 	uredislog->iovec[5].iov_len = len;
198 
199 	ret = writev(uredislog->fd, uredislog->iovec, 7);
200 	if (ret <= 0) {
201 		close(uredislog->fd);
202 		uredislog->fd = -1;
203 		return -1;
204 	}
205 
206 	uwsgi_redis_logger_discard_response(uredislog);
207 
208 	return ret;
209 
210 }
211 
uwsgi_redislog_register()212 void uwsgi_redislog_register() {
213 	uwsgi_register_logger("redislog", uwsgi_redis_logger);
214 }
215 
216 struct uwsgi_plugin redislog_plugin = {
217 
218         .name = "redislog",
219         .on_load = uwsgi_redislog_register,
220 
221 };
222 
223