* Please see COPYING for details
*
* Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
- * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
+ * Copyright (c) 2005-2010 Brenden Matthews, Philip Kovacs, et. al.
* (see AUTHORS)
* All rights reserved.
*
#include "conky.h"
#include "fs.h"
#include "logging.h"
+#include "net_stat.h"
+#include "specials.h"
+#include "timeinfo.h"
#include <ctype.h>
#include <errno.h>
#include <sys/time.h>
}
#endif /* HAVE_STRNDUP */
-void update_uname(void)
+int update_uname(void)
{
uname(&info.uname_s);
+ return 0;
}
double get_time(void)
*dest = '\0';
}
-/* network interface stuff */
-
-static struct net_stat netstats[16];
-
-struct net_stat *get_net_stat(const char *dev, void *free_at_crash1, void *free_at_crash2)
-{
- unsigned int i;
-
- if (!dev) {
- return 0;
- }
-
- /* find interface stat */
- for (i = 0; i < 16; i++) {
- if (netstats[i].dev && strcmp(netstats[i].dev, dev) == 0) {
- return &netstats[i];
- }
- }
-
- /* wasn't found? add it */
- for (i = 0; i < 16; i++) {
- if (netstats[i].dev == 0) {
- netstats[i].dev = strndup(dev, text_buffer_size);
- return &netstats[i];
- }
- }
-
- CRIT_ERR(free_at_crash1, free_at_crash2, "too many interfaces used (limit is 16)");
- return 0;
-}
-
-void clear_net_stats(void)
-{
- int i;
- for (i = 0; i < 16; i++) {
- if (netstats[i].dev) {
- free(netstats[i].dev);
- }
- }
- memset(netstats, 0, sizeof(netstats));
-}
-
-/* We should check if this is ok with OpenBSD and NetBSD as well. */
-int interface_up(const char *dev)
-{
- int fd;
- struct ifreq ifr;
-
- if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
- CRIT_ERR(NULL, NULL, "could not create sockfd");
- return 0;
- }
- strncpy(ifr.ifr_name, dev, IFNAMSIZ);
- if (ioctl(fd, SIOCGIFFLAGS, &ifr)) {
- /* if device does not exist, treat like not up */
- if (errno != ENODEV && errno != ENXIO)
- perror("SIOCGIFFLAGS");
- goto END_FALSE;
- }
-
- if (!(ifr.ifr_flags & IFF_UP)) /* iface is not up */
- goto END_FALSE;
- if (ifup_strictness == IFUP_UP)
- goto END_TRUE;
-
- if (!(ifr.ifr_flags & IFF_RUNNING))
- goto END_FALSE;
- if (ifup_strictness == IFUP_LINK)
- goto END_TRUE;
-
- if (ioctl(fd, SIOCGIFADDR, &ifr)) {
- perror("SIOCGIFADDR");
- goto END_FALSE;
- }
- if (((struct sockaddr_in *)&(ifr.ifr_ifru.ifru_addr))->sin_addr.s_addr)
- goto END_TRUE;
-
-END_FALSE:
- close(fd);
- return 0;
-END_TRUE:
- close(fd);
- return 1;
-}
-
-void free_dns_data(void)
-{
- int i;
- struct dns_data *data = &info.nameserver_info;
- for (i = 0; i < data->nscount; i++)
- free(data->ns_list[i]);
- if (data->ns_list)
- free(data->ns_list);
- memset(data, 0, sizeof(struct dns_data));
-}
-
-//static double last_dns_update;
-
-void update_dns_data(void)
-{
- FILE *fp;
- char line[256];
- struct dns_data *data = &info.nameserver_info;
-
- /* maybe updating too often causes higher load because of /etc lying on a real FS
- if (current_update_time - last_dns_update < 10.0)
- return;
- else
- last_dns_update = current_update_time;
- */
-
- free_dns_data();
-
- if ((fp = fopen("/etc/resolv.conf", "r")) == NULL)
- return;
- while(!feof(fp)) {
- if (fgets(line, 255, fp) == NULL) {
- break;
- }
- if (!strncmp(line, "nameserver ", 11)) {
- line[strlen(line) - 1] = '\0'; // remove trailing newline
- data->nscount++;
- data->ns_list = realloc(data->ns_list, data->nscount * sizeof(char *));
- data->ns_list[data->nscount - 1] = strndup(line + 11, text_buffer_size);
- }
- }
- fclose(fp);
-}
-
void format_seconds(char *buf, unsigned int n, long seconds)
{
long days;
int hours, minutes;
+ if (times_in_seconds()) {
+ snprintf(buf, n, "%ld", seconds);
+ return;
+ }
+
days = seconds / 86400;
seconds %= 86400;
hours = seconds / 3600;
long days;
int hours, minutes;
+ if (times_in_seconds()) {
+ snprintf(buf, n, "%ld", seconds);
+ return;
+ }
+
days = seconds / 86400;
seconds %= 86400;
hours = seconds / 3600;
* Populated while initialising text objects in construct_text_object(). */
static struct update_cb {
struct update_cb *next;
- void (*func)(void);
+ int (*func)(void);
pthread_t thread;
sem_t start_wait, end_wait;
- volatile char cancel;
+
+ /* set to 1 when starting the thread
+ * set to 0 to request thread termination */
+ volatile char running;
} update_cb_head = {
.next = NULL,
+ .func = NULL,
};
-static void *run_update_callback(void *);
+static void *run_update_callback(void *) __attribute__((noreturn));
+
+static int threading_started = 0;
/* Register an update callback. Don't allow duplicates, to minimise side
* effects and overhead. */
-void add_update_callback(void (*func)(void))
+void add_update_callback(int (*func)(void))
{
struct update_cb *uc = &update_cb_head;
sem_init(&uc->start_wait, 0, 0);
sem_init(&uc->end_wait, 0, 0);
- pthread_create(&uc->thread, NULL, &run_update_callback, uc);
+ if (threading_started) {
+ if (!uc->running) {
+ uc->running = 1;
+ pthread_create(&uc->thread, NULL, &run_update_callback, uc);
+ }
+ }
}
/* Free the list element uc and all decendants recursively. */
if (uc->next)
__free_update_callbacks(uc->next);
- /* send cancellation request, then trigger and join the thread */
- uc->cancel = 1;
- sem_post(&uc->start_wait);
- pthread_join(uc->thread, NULL);
+ if (uc->running) {
+ /* send cancellation request, then trigger and join the thread */
+ uc->running = 0;
+ sem_post(&uc->start_wait);
+ }
+ if (pthread_join(uc->thread, NULL)) {
+ NORM_ERR("Error destroying thread");
+ }
/* finally destroy the semaphores */
sem_destroy(&uc->start_wait);
update_cb_head.next = NULL;
}
+/* We cannot start threads before we forked to background, because the threads
+ * would remain in the wrong process. Because of that, add_update_callback()
+ * doesn't create threads before start_update_threading() is called.
+ * start_update_threading() starts all threads previously registered, and sets a
+ * flag so that future threads are automagically started by
+ * add_update_callback().
+ * This text is almost longer than the actual code.
+ */
+void start_update_threading(void)
+{
+ struct update_cb *uc;
+
+ threading_started = 1;
+
+ for (uc = update_cb_head.next; uc; uc = uc->next) {
+ if (!uc->running) {
+ uc->running = 1;
+ pthread_create(&uc->thread, NULL, &run_update_callback, uc);
+ }
+ }
+}
+
static void *run_update_callback(void *data)
{
struct update_cb *ucb = data;
+ if (!ucb || !ucb->func) pthread_exit(NULL);
+
while (1) {
- sem_wait(&ucb->start_wait);
- if(ucb->cancel)
- return NULL;
- (*ucb->func)();
- sem_post(&ucb->end_wait);
+ if (sem_wait(&ucb->start_wait)) pthread_exit(NULL);
+ if (ucb->running == 0) pthread_exit(NULL);
+ if((*ucb->func)()) {
+ ucb->next = ucb; //this is normally not be possible, so we use it to show that there was a critical error
+ sem_post(&ucb->end_wait);
+ sem_post(&ucb->end_wait);
+ pthread_exit(NULL);
+ }
+ if (sem_post(&ucb->end_wait)) pthread_exit(NULL);
}
}
#ifdef HAVE_OPENMP
#pragma omp parallel for schedule(dynamic,10)
#endif /* HAVE_OPENMP */
- for (i = 0; i < 16; i++) {
+ for (i = 0; i < MAX_NET_INTERFACES; i++) {
if (netstats[i].dev) {
netstats[i].up = 0;
netstats[i].recv_speed = 0.0;
prepare_update();
- for (uc = update_cb_head.next; uc; uc = uc->next)
- sem_post(&uc->start_wait);
+ for (uc = update_cb_head.next; uc; uc = uc->next) {
+ if (sem_post(&uc->start_wait)) {
+ NORM_ERR("Semaphore error");
+ }
+ }
/* need to synchronise here, otherwise locking is needed (as data
* would be printed with some update callbacks still running) */
- for (uc = update_cb_head.next; uc; uc = uc->next)
+ for (uc = update_cb_head.next; uc; uc = uc->next) {
sem_wait(&uc->end_wait);
+ if(uc == uc->next) {
+ pthread_join(uc->thread, NULL);
+ free(uc);
+ exit(EXIT_FAILURE);
+ }
+ }
/* XXX: move the following into the update_meminfo() functions? */
if (no_buffers) {
}
}
+void scan_loadavg_arg(struct text_object *obj, const char *arg)
+{
+ obj->data.i = 0;
+ if (arg && !arg[1] && isdigit(arg[0])) {
+ obj->data.i = atoi(arg);
+ if (obj->data.i > 3 || obj->data.i < 1) {
+ NORM_ERR("loadavg arg needs to be in range (1,3)");
+ obj->data.i = 0;
+ }
+ }
+ /* convert to array index (or the default (-1)) */
+ obj->data.i--;
+}
+
+void print_loadavg(struct text_object *obj, char *p, int p_max_size)
+{
+ float *v = info.loadavg;
+
+ if (obj->data.i < 0) {
+ snprintf(p, p_max_size, "%.2f %.2f %.2f", v[0], v[1], v[2]);
+ } else {
+ snprintf(p, p_max_size, "%.2f", v[obj->data.i]);
+ }
+}
+
+#ifdef X11
+void scan_loadgraph_arg(struct text_object *obj, const char *arg)
+{
+ char *buf = 0;
+
+ buf = scan_graph(obj, arg, 0);
+ if (buf)
+ free(buf);
+}
+
+void print_loadgraph(struct text_object *obj, char *p, int p_max_size)
+{
+ new_graph(obj, p, p_max_size, info.loadavg[0]);
+}
+#endif /* X11 */