Fixed child wait.
[dana/openbox.git] / plugins / menu / timed_menu.c
1 #include <glib.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <stdio.h>
7
8 #include "kernel/menu.h"
9 #include "kernel/timer.h"
10 #include "timed_menu.h"
11 #include "kernel/action.h"
12 #include "kernel/event.h"
13
14 #define TIMED_MENU(m) ((Menu *)m)
15 #define TIMED_MENU_DATA(m) ((Timed_Menu_Data *)((Menu *)m)->plugin_data)
16 static char *PLUGIN_NAME = "timed_menu";
17
18 typedef enum {
19     TIMED_MENU_PIPE
20 } Timed_Menu_Type;
21
22 /* we can use various GIO channels to support reading menus (can we add them to
23    the event loop? )
24    stat() based update
25    exec() based read
26 */
27 typedef struct {
28     Timed_Menu_Type type;
29     Timer *timer; /* timer code handles free */
30     char *command; /* for the PIPE */
31     char *buf;
32     unsigned long buflen;
33     int fd;
34     pid_t pid;
35 } Timed_Menu_Data;
36
37
38 void plugin_setup_config() { }
39 void plugin_startup()
40 { }
41 void plugin_shutdown() { }
42
43 void timed_menu_clean_up(Menu *m) {
44     if (TIMED_MENU_DATA(m)->buf != NULL) {
45         fprintf(stderr, "%s", TIMED_MENU_DATA(m)->buf);
46         g_free(TIMED_MENU_DATA(m)->buf);
47         TIMED_MENU_DATA(m)->buf = NULL;
48     }
49
50     TIMED_MENU_DATA(m)->buflen = 0;
51
52     if (TIMED_MENU_DATA(m)->fd != -1) {
53         event_remove_fd(TIMED_MENU_DATA(m)->fd);
54         close(TIMED_MENU_DATA(m)->fd);
55         TIMED_MENU_DATA(m)->fd = -1;
56     }
57
58     if (TIMED_MENU_DATA(m)->pid != -1) {
59         waitpid(TIMED_MENU_DATA(m)->pid, NULL, 0);
60         TIMED_MENU_DATA(m)->pid = -1;
61     }
62 }
63
64 void timed_menu_read_pipe(int fd, Menu *menu)
65 {
66     char *tmpbuf = NULL;
67     unsigned long num_read;
68 #ifdef DEBUG
69     Timed_Menu_Data *d = TIMED_MENU_DATA(menu);
70 #endif
71
72     unsigned long num_realloc;
73     /* if we have less than a quarter BUFSIZ left, allocate more */
74     num_realloc = (BUFSIZ - (TIMED_MENU_DATA(menu)->buflen % BUFSIZ) <
75                    BUFSIZ >> 2) ?
76                   0 : BUFSIZ;
77     
78     tmpbuf = g_try_realloc(TIMED_MENU_DATA(menu)->buf,             
79                            TIMED_MENU_DATA(menu)->buflen + num_realloc);
80
81     if (tmpbuf == NULL) {
82         g_warning("Unable to allocate memory for read()");
83         return;
84     }
85     
86     TIMED_MENU_DATA(menu)->buf = tmpbuf;
87     
88     num_read = read(fd,
89                     TIMED_MENU_DATA(menu)->buf + TIMED_MENU_DATA(menu)->buflen,
90                     num_realloc);
91     if (num_read == 0) {
92         unsigned long count = 0;
93         char *found = NULL;
94         menu->invalid = TRUE;
95         menu_clear(menu);
96
97         /* TEMP: list them */
98         while (NULL !=
99                (found = strchr(&TIMED_MENU_DATA(menu)->buf[count], '\n'))) {
100             TIMED_MENU_DATA(menu)->buf
101                 [found - TIMED_MENU_DATA(menu)->buf] = '\0';
102             menu_add_entry(menu,
103                 menu_entry_new_separator
104                 (&TIMED_MENU_DATA(menu)->buf[count]));
105             count = found - TIMED_MENU_DATA(menu)->buf + 1;
106         }
107             
108
109         TIMED_MENU_DATA(menu)->buf[TIMED_MENU_DATA(menu)->buflen] = '\0';
110         timed_menu_clean_up(menu);
111     } else if (num_read > 0) {
112         TIMED_MENU_DATA(menu)->buflen += num_read;
113         TIMED_MENU_DATA(menu)->buf[TIMED_MENU_DATA(menu)->buflen] = '\0';
114     } else { /* num_read < 1 */
115         g_warning("Error on read %s", strerror(errno));
116         timed_menu_clean_up(menu);
117     }
118 }
119
120 void timed_menu_timeout_handler(Menu *data)
121 {
122     Action *a;
123
124     if (!data->shown && TIMED_MENU_DATA(data)->fd == -1) {
125         switch (TIMED_MENU_DATA(data)->type) {
126             case (TIMED_MENU_PIPE):
127             {
128                 /* if the menu is not shown, run a process and use its output
129                    as menu */
130
131                 /* I hate you glib in all your hideous forms */
132                 char *args[] = {"/bin/sh", "-c", TIMED_MENU_DATA(data)->command,
133                                 NULL};
134                 int child_stdout;
135                 if (g_spawn_async_with_pipes(
136                         NULL,
137                         args,
138                         NULL,
139                         G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
140                         NULL,
141                         &TIMED_MENU_DATA(data)->pid,                        
142                         NULL,
143                         NULL,
144                         &child_stdout,
145                         NULL,
146                         NULL)) {
147                     event_fd_handler *h = g_new(event_fd_handler, 1);
148                     TIMED_MENU_DATA(data)->fd = h->fd = child_stdout;
149                     h->handler = timed_menu_read_pipe;
150                     h->data = data;
151                     event_add_fd_handler(h);
152                 } else {
153                     g_warning("unable to spawn child");
154                 }
155                     
156                 break;
157             }
158         }
159     }
160 }
161
162 void *plugin_create()
163 {
164     Timed_Menu_Data *d = g_new(Timed_Menu_Data, 1);
165     Menu *m = menu_new("", PLUGIN_NAME, NULL);
166     
167     m->plugin = PLUGIN_NAME;
168
169     d->type = TIMED_MENU_PIPE;
170     d->timer = timer_start(60000000, &timed_menu_timeout_handler, m);
171     d->command = "find /media -name *.m3u";
172     d->buf = NULL;
173     d->buflen = 0;
174     d->fd = -1;
175     d->pid = -1;
176     
177     m->plugin_data = (void *)d;
178
179     timed_menu_timeout_handler(m);
180     return (void *)m;
181 }
182
183 void plugin_destroy (void *m)
184 {
185     /* this will be freed by timer_* */
186     timer_stop( ((Timed_Menu_Data *)TIMED_MENU(m)->plugin_data)->timer);
187     
188     g_free( TIMED_MENU(m)->plugin_data );
189 }