5d64f89c09b9cb704a84ea9cef33d6d856ae2490
[monky] / src / ccurl_thread.c
1 /*
2  * Conky, a system monitor, based on torsmo
3  *
4  * Please see COPYING for details
5  *
6  * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
7  *      (see AUTHORS)
8  * All rights reserved.
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  */
23
24 #include "conky.h"
25 #include "logging.h"
26 #include "ccurl_thread.h"
27
28 #ifdef DEBUG
29 #include <assert.h>
30 #endif /* DEBUG */
31
32 #include <curl/curl.h>
33 #include <curl/types.h>
34 #include <curl/easy.h>
35
36 /*
37  * The following code is the conky curl thread lib, which can be re-used to
38  * create any curl-based object (see weather and rss).  Below is an
39  * implementation of a curl-only object ($curl) which can also be used as an
40  * example.
41  */
42 typedef struct _ccurl_memory_t {
43         char *memory;
44         size_t size;
45 } ccurl_memory_t;
46
47 /* finds a location based on uri in the list provided */
48 ccurl_location_t *ccurl_find_location(ccurl_location_t **locations_head, char *uri)
49 {
50         ccurl_location_t *tail = *locations_head;
51         ccurl_location_t *new = 0;
52         while (tail) {
53                 if (tail->uri &&
54                                 strcmp(tail->uri, uri) == EQUAL) {
55                         return tail;
56                 }
57                 tail = tail->next;
58         }
59         if (!tail) { /* new location!!!!!!! */
60                 DBGP("new curl location: '%s'", uri);
61                 new = malloc(sizeof(ccurl_location_t));
62                 memset(new, 0, sizeof(ccurl_location_t));
63                 new->uri = strndup(uri, text_buffer_size);
64                 tail = *locations_head;
65                 while (tail && tail->next) {
66                         tail = tail->next;
67                 }
68                 if (!tail) {
69                         /* omg the first one!!!!!!! */
70                         *locations_head = new;
71                 } else {
72                         tail->next = new;
73                 }
74         }
75         return new;
76 }
77
78 /* iterates over the list provided, frees stuff (list item, uri, result) */
79 void ccurl_free_locations(ccurl_location_t **locations_head)
80 {
81         ccurl_location_t *tail = *locations_head;
82         ccurl_location_t *last = 0;
83
84         while (tail) {
85                 if (tail->uri) free(tail->uri);
86                 if (tail->result) free(tail->result);
87                 last = tail;
88                 tail = tail->next;
89                 free(last);
90         }
91         *locations_head = 0;
92 }
93
94 /* callback used by curl for writing the received data */
95 size_t ccurl_write_memory_callback(void *ptr, size_t size, size_t nmemb, void *data)
96 {
97         size_t realsize = size * nmemb;
98         ccurl_memory_t *mem = (ccurl_memory_t*)data;
99
100         mem->memory = (char *) realloc(mem->memory, mem->size + realsize + 1);
101         if (mem->memory) {
102                 memcpy(&(mem->memory[mem->size]), ptr, realsize);
103                 mem->size += realsize;
104                 mem->memory[mem->size] = 0;
105         }
106         return realsize;
107 }
108
109
110 /* fetch our datums */
111 void ccurl_fetch_data(ccurl_location_t *curloc)
112 {
113         CURL *curl = NULL;
114         CURLcode res;
115
116         // curl temps
117         ccurl_memory_t chunk;
118
119         chunk.memory = NULL;
120         chunk.size = 0;
121
122         curl = curl_easy_init();
123         if (curl) {
124                 DBGP("reading curl data from '%s'", curloc->uri);
125                 curl_easy_setopt(curl, CURLOPT_URL, curloc->uri);
126                 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
127                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ccurl_write_memory_callback);
128                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &chunk);
129                 curl_easy_setopt(curl, CURLOPT_USERAGENT, "conky-curl/1.0");
130
131                 res = curl_easy_perform(curl);
132                 if (res == CURLE_OK && chunk.size) {
133                         timed_thread_lock(curloc->p_timed_thread);
134                         (*curloc->process_function)(curloc->result, chunk.memory);
135                         timed_thread_unlock(curloc->p_timed_thread);
136                         free(chunk.memory);
137                 } else {
138                         ERR("curl: no data from server");
139                 }
140
141                 curl_easy_cleanup(curl);
142         }
143 }
144
145 void *ccurl_thread(void *) __attribute__((noreturn));
146
147 void ccurl_init_thread(ccurl_location_t *curloc, int interval)
148 {
149 #ifdef DEBUG
150         assert(curloc->result);
151 #endif /* DEBUG */
152         curloc->p_timed_thread =
153                 timed_thread_create(&ccurl_thread,
154                                 (void *)curloc, interval * 1000000);
155
156         if (!curloc->p_timed_thread) {
157                 ERR("curl thread: error creating timed thread");
158         }
159         timed_thread_register(curloc->p_timed_thread,
160                         &curloc->p_timed_thread);
161         if (timed_thread_run(curloc->p_timed_thread)) {
162                 ERR("curl thread: error running timed thread");
163         }
164 }
165
166 void *ccurl_thread(void *arg)
167 {
168         ccurl_location_t *curloc = (ccurl_location_t*)arg;
169
170         while (1) {
171                 ccurl_fetch_data(curloc);
172                 if (timed_thread_test(curloc->p_timed_thread, 0)) {
173                         timed_thread_exit(curloc->p_timed_thread);
174                 }
175         }
176         /* never reached */
177 }
178
179
180 /*
181  * This is where the $curl section begins.
182  */
183
184 /* internal location pointer for use by $curl, no touchy */
185 static ccurl_location_t *ccurl_locations_head = 0;
186
187 /* used to free data used by $curl */
188 void ccurl_free_info(void)
189 {
190         ccurl_free_locations(&ccurl_locations_head);
191 }
192
193 /* straight copy, used by $curl */
194 void ccurl_parse_data(void *result, const char *data)
195 {
196         strncpy(result, data, max_user_text);
197 }
198
199 /* prints result data to text buffer, used by $curl */
200 void ccurl_process_info(char *p, int p_max_size, char *uri, int interval)
201 {
202         ccurl_location_t *curloc = ccurl_find_location(&ccurl_locations_head, uri);
203         if (!curloc->p_timed_thread) {
204                 curloc->result = malloc(max_user_text);
205                 memset(curloc->result, 0, max_user_text);
206                 curloc->process_function = &ccurl_parse_data;
207                 ccurl_init_thread(curloc, interval);
208                 if (!curloc->p_timed_thread) {
209                         ERR("error setting up curl thread");
210                 }
211         }
212
213         timed_thread_lock(curloc->p_timed_thread);
214         strncpy(p, curloc->result, p_max_size);
215         timed_thread_unlock(curloc->p_timed_thread);
216 }
217