|
MythTV
0.27pre
|
00001 /* 00002 * hdhomerun_debug.c 00003 * 00004 * Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>. 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public 00008 * License as published by the Free Software Foundation; either 00009 * version 3 of the License, or (at your option) any later version. 00010 * 00011 * This library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public 00017 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 00018 * 00019 * As a special exception to the GNU Lesser General Public License, 00020 * you may link, statically or dynamically, an application with a 00021 * publicly distributed version of the Library to produce an 00022 * executable file containing portions of the Library, and 00023 * distribute that executable file under terms of your choice, 00024 * without any of the additional requirements listed in clause 4 of 00025 * the GNU Lesser General Public License. 00026 * 00027 * By "a publicly distributed version of the Library", we mean 00028 * either the unmodified Library as distributed by Silicondust, or a 00029 * modified version of the Library that is distributed under the 00030 * conditions defined in the GNU Lesser General Public License. 00031 */ 00032 00033 /* 00034 * The debug logging includes optional support for connecting to the 00035 * Silicondust support server. This option should not be used without 00036 * being explicitly enabled by the user. Debug information should be 00037 * limited to information useful to diagnosing a problem. 00038 * - Silicondust. 00039 */ 00040 00041 #include "hdhomerun.h" 00042 00043 #if !defined(HDHOMERUN_DEBUG_HOST) 00044 #define HDHOMERUN_DEBUG_HOST "debug.silicondust.com" 00045 #endif 00046 #if !defined(HDHOMERUN_DEBUG_PORT) 00047 #define HDHOMERUN_DEBUG_PORT 8002 00048 #endif 00049 00050 #define HDHOMERUN_DEBUG_CONNECT_RETRY_TIME 30000 00051 #define HDHOMERUN_DEBUG_CONNECT_TIMEOUT 10000 00052 #define HDHOMERUN_DEBUG_SEND_TIMEOUT 10000 00053 00054 struct hdhomerun_debug_message_t 00055 { 00056 struct hdhomerun_debug_message_t *next; 00057 struct hdhomerun_debug_message_t *prev; 00058 char buffer[2048]; 00059 }; 00060 00061 struct hdhomerun_debug_t 00062 { 00063 pthread_t thread; 00064 volatile bool_t enabled; 00065 volatile bool_t terminate; 00066 char *prefix; 00067 00068 pthread_mutex_t print_lock; 00069 pthread_mutex_t queue_lock; 00070 pthread_mutex_t send_lock; 00071 00072 struct hdhomerun_debug_message_t *queue_head; 00073 struct hdhomerun_debug_message_t *queue_tail; 00074 uint32_t queue_depth; 00075 00076 uint64_t connect_delay; 00077 00078 char *file_name; 00079 FILE *file_fp; 00080 hdhomerun_sock_t sock; 00081 }; 00082 00083 static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg); 00084 00085 struct hdhomerun_debug_t *hdhomerun_debug_create(void) 00086 { 00087 struct hdhomerun_debug_t *dbg = (struct hdhomerun_debug_t *)calloc(1, sizeof(struct hdhomerun_debug_t)); 00088 if (!dbg) { 00089 return NULL; 00090 } 00091 00092 dbg->sock = HDHOMERUN_SOCK_INVALID; 00093 00094 pthread_mutex_init(&dbg->print_lock, NULL); 00095 pthread_mutex_init(&dbg->queue_lock, NULL); 00096 pthread_mutex_init(&dbg->send_lock, NULL); 00097 00098 if (pthread_create(&dbg->thread, NULL, &hdhomerun_debug_thread_execute, dbg) != 0) { 00099 free(dbg); 00100 return NULL; 00101 } 00102 00103 return dbg; 00104 } 00105 00106 void hdhomerun_debug_destroy(struct hdhomerun_debug_t *dbg) 00107 { 00108 if (!dbg) { 00109 return; 00110 } 00111 00112 dbg->terminate = TRUE; 00113 pthread_join(dbg->thread, NULL); 00114 00115 if (dbg->prefix) { 00116 free(dbg->prefix); 00117 } 00118 if (dbg->file_name) { 00119 free(dbg->file_name); 00120 } 00121 if (dbg->file_fp) { 00122 fclose(dbg->file_fp); 00123 } 00124 if (dbg->sock != HDHOMERUN_SOCK_INVALID) { 00125 hdhomerun_sock_destroy(dbg->sock); 00126 } 00127 00128 free(dbg); 00129 } 00130 00131 /* Send lock held by caller */ 00132 static void hdhomerun_debug_close_internal(struct hdhomerun_debug_t *dbg) 00133 { 00134 if (dbg->file_fp) { 00135 fclose(dbg->file_fp); 00136 dbg->file_fp = NULL; 00137 } 00138 00139 if (dbg->sock != HDHOMERUN_SOCK_INVALID) { 00140 hdhomerun_sock_destroy(dbg->sock); 00141 dbg->sock = HDHOMERUN_SOCK_INVALID; 00142 } 00143 } 00144 00145 void hdhomerun_debug_close(struct hdhomerun_debug_t *dbg, uint64_t timeout) 00146 { 00147 if (!dbg) { 00148 return; 00149 } 00150 00151 if (timeout > 0) { 00152 hdhomerun_debug_flush(dbg, timeout); 00153 } 00154 00155 pthread_mutex_lock(&dbg->send_lock); 00156 hdhomerun_debug_close_internal(dbg); 00157 dbg->connect_delay = 0; 00158 pthread_mutex_unlock(&dbg->send_lock); 00159 } 00160 00161 void hdhomerun_debug_set_filename(struct hdhomerun_debug_t *dbg, const char *filename) 00162 { 00163 if (!dbg) { 00164 return; 00165 } 00166 00167 pthread_mutex_lock(&dbg->send_lock); 00168 00169 if (!filename && !dbg->file_name) { 00170 pthread_mutex_unlock(&dbg->send_lock); 00171 return; 00172 } 00173 if (filename && dbg->file_name) { 00174 if (strcmp(filename, dbg->file_name) == 0) { 00175 pthread_mutex_unlock(&dbg->send_lock); 00176 return; 00177 } 00178 } 00179 00180 hdhomerun_debug_close_internal(dbg); 00181 dbg->connect_delay = 0; 00182 00183 if (dbg->file_name) { 00184 free(dbg->file_name); 00185 dbg->file_name = NULL; 00186 } 00187 if (filename) { 00188 dbg->file_name = strdup(filename); 00189 } 00190 00191 pthread_mutex_unlock(&dbg->send_lock); 00192 } 00193 00194 void hdhomerun_debug_set_prefix(struct hdhomerun_debug_t *dbg, const char *prefix) 00195 { 00196 if (!dbg) { 00197 return; 00198 } 00199 00200 pthread_mutex_lock(&dbg->print_lock); 00201 00202 if (dbg->prefix) { 00203 free(dbg->prefix); 00204 dbg->prefix = NULL; 00205 } 00206 00207 if (prefix) { 00208 dbg->prefix = strdup(prefix); 00209 } 00210 00211 pthread_mutex_unlock(&dbg->print_lock); 00212 } 00213 00214 void hdhomerun_debug_enable(struct hdhomerun_debug_t *dbg) 00215 { 00216 if (!dbg) { 00217 return; 00218 } 00219 00220 dbg->enabled = TRUE; 00221 } 00222 00223 void hdhomerun_debug_disable(struct hdhomerun_debug_t *dbg) 00224 { 00225 if (!dbg) { 00226 return; 00227 } 00228 00229 dbg->enabled = FALSE; 00230 } 00231 00232 bool_t hdhomerun_debug_enabled(struct hdhomerun_debug_t *dbg) 00233 { 00234 if (!dbg) { 00235 return FALSE; 00236 } 00237 00238 return dbg->enabled; 00239 } 00240 00241 void hdhomerun_debug_flush(struct hdhomerun_debug_t *dbg, uint64_t timeout) 00242 { 00243 if (!dbg) { 00244 return; 00245 } 00246 00247 timeout = getcurrenttime() + timeout; 00248 00249 while (getcurrenttime() < timeout) { 00250 pthread_mutex_lock(&dbg->queue_lock); 00251 struct hdhomerun_debug_message_t *message = dbg->queue_tail; 00252 pthread_mutex_unlock(&dbg->queue_lock); 00253 00254 if (!message) { 00255 return; 00256 } 00257 00258 msleep_approx(10); 00259 } 00260 } 00261 00262 void hdhomerun_debug_printf(struct hdhomerun_debug_t *dbg, const char *fmt, ...) 00263 { 00264 va_list args; 00265 va_start(args, fmt); 00266 hdhomerun_debug_vprintf(dbg, fmt, args); 00267 va_end(args); 00268 } 00269 00270 void hdhomerun_debug_vprintf(struct hdhomerun_debug_t *dbg, const char *fmt, va_list args) 00271 { 00272 if (!dbg) { 00273 return; 00274 } 00275 if (!dbg->enabled) { 00276 return; 00277 } 00278 00279 struct hdhomerun_debug_message_t *message = (struct hdhomerun_debug_message_t *)malloc(sizeof(struct hdhomerun_debug_message_t)); 00280 if (!message) { 00281 return; 00282 } 00283 00284 char *ptr = message->buffer; 00285 char *end = message->buffer + sizeof(message->buffer) - 2; 00286 *end = 0; 00287 00288 /* 00289 * Timestamp. 00290 */ 00291 time_t current_time = time(NULL); 00292 ptr += strftime(ptr, end - ptr, "%Y%m%d-%H:%M:%S ", localtime(¤t_time)); 00293 if (ptr > end) { 00294 ptr = end; 00295 } 00296 00297 /* 00298 * Debug prefix. 00299 */ 00300 pthread_mutex_lock(&dbg->print_lock); 00301 00302 if (dbg->prefix) { 00303 int len = snprintf(ptr, end - ptr, "%s ", dbg->prefix); 00304 len = (len <= 0) ? 0 : len; 00305 ptr += len; 00306 if (ptr > end) { 00307 ptr = end; 00308 } 00309 } 00310 00311 pthread_mutex_unlock(&dbg->print_lock); 00312 00313 /* 00314 * Message text. 00315 */ 00316 int len = vsnprintf(ptr, end - ptr, fmt, args); 00317 len = (len < 0) ? 0 : len; /* len does not include null */ 00318 ptr += len; 00319 if (ptr > end) { 00320 ptr = end; 00321 } 00322 00323 /* 00324 * Force newline. 00325 */ 00326 if ((ptr[-1] != '\n') && (ptr + 1 <= end)) { 00327 *ptr++ = '\n'; 00328 } 00329 00330 /* 00331 * Force NULL. 00332 */ 00333 if (ptr + 1 > end) { 00334 ptr = end - 1; 00335 } 00336 *ptr++ = 0; 00337 00338 /* 00339 * Enqueue. 00340 */ 00341 pthread_mutex_lock(&dbg->queue_lock); 00342 00343 message->prev = NULL; 00344 message->next = dbg->queue_head; 00345 dbg->queue_head = message; 00346 if (message->next) { 00347 message->next->prev = message; 00348 } else { 00349 dbg->queue_tail = message; 00350 } 00351 dbg->queue_depth++; 00352 00353 pthread_mutex_unlock(&dbg->queue_lock); 00354 } 00355 00356 /* Send lock held by caller */ 00357 static bool_t hdhomerun_debug_output_message_file(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message) 00358 { 00359 if (!dbg->file_fp) { 00360 uint64_t current_time = getcurrenttime(); 00361 if (current_time < dbg->connect_delay) { 00362 return FALSE; 00363 } 00364 dbg->connect_delay = current_time + 30*1000; 00365 00366 dbg->file_fp = fopen(dbg->file_name, "a"); 00367 if (!dbg->file_fp) { 00368 return FALSE; 00369 } 00370 } 00371 00372 fprintf(dbg->file_fp, "%s", message->buffer); 00373 fflush(dbg->file_fp); 00374 00375 return TRUE; 00376 } 00377 00378 /* Send lock held by caller */ 00379 static bool_t hdhomerun_debug_output_message_sock(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message) 00380 { 00381 if (dbg->sock == HDHOMERUN_SOCK_INVALID) { 00382 uint64_t current_time = getcurrenttime(); 00383 if (current_time < dbg->connect_delay) { 00384 return FALSE; 00385 } 00386 dbg->connect_delay = current_time + HDHOMERUN_DEBUG_CONNECT_RETRY_TIME; 00387 00388 dbg->sock = hdhomerun_sock_create_tcp(); 00389 if (dbg->sock == HDHOMERUN_SOCK_INVALID) { 00390 return FALSE; 00391 } 00392 00393 uint32_t remote_addr = hdhomerun_sock_getaddrinfo_addr(dbg->sock, HDHOMERUN_DEBUG_HOST); 00394 if (remote_addr == 0) { 00395 hdhomerun_debug_close_internal(dbg); 00396 return FALSE; 00397 } 00398 00399 if (!hdhomerun_sock_connect(dbg->sock, remote_addr, HDHOMERUN_DEBUG_PORT, HDHOMERUN_DEBUG_CONNECT_TIMEOUT)) { 00400 hdhomerun_debug_close_internal(dbg); 00401 return FALSE; 00402 } 00403 } 00404 00405 size_t length = strlen(message->buffer); 00406 if (!hdhomerun_sock_send(dbg->sock, message->buffer, length, HDHOMERUN_DEBUG_SEND_TIMEOUT)) { 00407 hdhomerun_debug_close_internal(dbg); 00408 return FALSE; 00409 } 00410 00411 return TRUE; 00412 } 00413 00414 static bool_t hdhomerun_debug_output_message(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message) 00415 { 00416 pthread_mutex_lock(&dbg->send_lock); 00417 00418 bool_t ret; 00419 if (dbg->file_name) { 00420 ret = hdhomerun_debug_output_message_file(dbg, message); 00421 } else { 00422 ret = hdhomerun_debug_output_message_sock(dbg, message); 00423 } 00424 00425 pthread_mutex_unlock(&dbg->send_lock); 00426 return ret; 00427 } 00428 00429 static void hdhomerun_debug_pop_and_free_message(struct hdhomerun_debug_t *dbg) 00430 { 00431 pthread_mutex_lock(&dbg->queue_lock); 00432 00433 struct hdhomerun_debug_message_t *message = dbg->queue_tail; 00434 dbg->queue_tail = message->prev; 00435 if (message->prev) { 00436 message->prev->next = NULL; 00437 } else { 00438 dbg->queue_head = NULL; 00439 } 00440 dbg->queue_depth--; 00441 00442 pthread_mutex_unlock(&dbg->queue_lock); 00443 00444 free(message); 00445 } 00446 00447 static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg) 00448 { 00449 struct hdhomerun_debug_t *dbg = (struct hdhomerun_debug_t *)arg; 00450 00451 while (!dbg->terminate) { 00452 00453 pthread_mutex_lock(&dbg->queue_lock); 00454 struct hdhomerun_debug_message_t *message = dbg->queue_tail; 00455 uint32_t queue_depth = dbg->queue_depth; 00456 pthread_mutex_unlock(&dbg->queue_lock); 00457 00458 if (!message) { 00459 msleep_approx(250); 00460 continue; 00461 } 00462 00463 if (queue_depth > 1024) { 00464 hdhomerun_debug_pop_and_free_message(dbg); 00465 continue; 00466 } 00467 00468 if (!hdhomerun_debug_output_message(dbg, message)) { 00469 msleep_approx(250); 00470 continue; 00471 } 00472 00473 hdhomerun_debug_pop_and_free_message(dbg); 00474 } 00475 00476 return 0; 00477 }
1.8.0