Mercurial > ~mikael > mcabber > hg
comparison mcabber/src/fifo.c @ 1653:fca9a4c17432
Improve UI latency and CPU usage
Remove main_loop(), and use GIOChannels for the FIFO system.
author | Myhailo Danylenko <isbear@ukrpost.net> |
---|---|
date | Tue, 01 Dec 2009 21:06:06 +0100 |
parents | dcd5d4c75199 |
children |
comparison
equal
deleted
inserted
replaced
1652:8036750d0169 | 1653:fca9a4c17432 |
---|---|
1 /* | 1 /* |
2 * fifo.c -- Read commands from a named pipe | 2 * fifo.c -- Read commands from a named pipe |
3 * | 3 * |
4 * Copyright (C) 2008,2009 Mikael Berthe <mikael@lilotux.net> | 4 * Copyright (C) 2008,2009 Mikael Berthe <mikael@lilotux.net> |
5 * Copyrigth (C) 2009 Myhailo Danylenko <isbear@ukrpost.net> | |
5 * | 6 * |
6 * This program is free software; you can redistribute it and/or modify | 7 * This program is free software; you can redistribute it and/or modify |
7 * it under the terms of the GNU General Public License as published by | 8 * it under the terms of the GNU General Public License as published by |
8 * the Free Software Foundation; either version 2 of the License, or (at | 9 * the Free Software Foundation; either version 2 of the License, or (at |
9 * your option) any later version. | 10 * your option) any later version. |
17 * along with this program; if not, write to the Free Software | 18 * along with this program; if not, write to the Free Software |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
19 * USA | 20 * USA |
20 */ | 21 */ |
21 | 22 |
22 #include <stdio.h> | |
23 #include <stdlib.h> | 23 #include <stdlib.h> |
24 #include <glib.h> | 24 #include <glib.h> |
25 #include <sys/types.h> | 25 #include <sys/types.h> |
26 #include <sys/stat.h> | 26 #include <sys/stat.h> |
27 #include <fcntl.h> | 27 #include <fcntl.h> |
28 #include <unistd.h> | 28 #include <unistd.h> |
29 #include <sys/time.h> | |
30 #include <errno.h> | 29 #include <errno.h> |
31 #include <fcntl.h> | 30 #include <fcntl.h> |
32 | 31 |
33 #include "commands.h" | 32 #include "commands.h" |
34 #include "logprint.h" | 33 #include "logprint.h" |
35 #include "utils.h" | 34 #include "utils.h" |
36 #include "settings.h" | 35 #include "settings.h" |
36 #include "main.h" | |
37 | 37 |
38 #include "hbuf.h" // For HBB_BLOCKSIZE | 38 static char *fifo_name = NULL; |
39 | 39 static GIOChannel *fifo_channel = NULL; |
40 static FILE *sfd; | |
41 static char *fifo_name; | |
42 | 40 |
43 static const char *FIFO_ENV_NAME = "MCABBER_FIFO"; | 41 static const char *FIFO_ENV_NAME = "MCABBER_FIFO"; |
44 | 42 |
45 // fifo_init(fifo_path) | 43 static gboolean attach_fifo(const char *name); |
46 // Create and open the FIFO file. | 44 |
47 // If fifo_path is NULL, reopen the current pipe. | 45 static guint fifo_callback(GIOChannel *channel, |
48 // Return 0 (success) or -1 (failure). | 46 GIOCondition condition, |
47 gpointer data) | |
48 { | |
49 if (condition & (G_IO_IN|G_IO_PRI)) { | |
50 GIOStatus chstat; | |
51 gchar *buf; | |
52 gsize endpos; | |
53 | |
54 chstat = g_io_channel_read_line(channel, &buf, NULL, &endpos, NULL); | |
55 if (chstat == G_IO_STATUS_ERROR || chstat == G_IO_STATUS_EOF) { | |
56 if (!attach_fifo(fifo_name)) | |
57 scr_LogPrint(LPRINT_LOGNORM, | |
58 "Reopening fifo failed! Fifo will not work from now!"); | |
59 return FALSE; | |
60 } | |
61 if (buf) { | |
62 guint logflag; | |
63 guint fifo_ignore = settings_opt_get_int("fifo_ignore"); | |
64 | |
65 if (endpos) | |
66 buf[endpos] = '\0'; | |
67 | |
68 if (settings_opt_get_int("fifo_hide_commands")) | |
69 logflag = LPRINT_LOG; | |
70 else | |
71 logflag = LPRINT_LOGNORM; | |
72 scr_LogPrint(logflag, "%s FIFO command: %s", | |
73 (fifo_ignore ? "Ignoring" : "Executing"), buf); | |
74 if (!fifo_ignore) { | |
75 if (process_command(buf, TRUE) == 255) | |
76 mcabber_set_terminate_ui(); | |
77 } | |
78 | |
79 g_free(buf); | |
80 } | |
81 } else if (condition & (G_IO_ERR|G_IO_NVAL|G_IO_HUP)) { | |
82 if (!attach_fifo(fifo_name)) | |
83 scr_LogPrint(LPRINT_LOGNORM, | |
84 "Reopening fifo failed! Fifo will not work from now!"); | |
85 return FALSE; | |
86 } | |
87 return TRUE; | |
88 } | |
89 | |
90 static void fifo_destroy_callback(gpointer data) | |
91 { | |
92 GIOChannel *channel = (GIOChannel *)data; | |
93 g_io_channel_unref(channel); | |
94 } | |
95 | |
96 static gboolean check_fifo(const char *name) | |
97 { | |
98 struct stat finfo; | |
99 if (stat(name, &finfo) == -1) { | |
100 /* some unknown error */ | |
101 if (errno != ENOENT) | |
102 return FALSE; | |
103 /* fifo not yet exists */ | |
104 if (mkfifo(name, S_IRUSR|S_IWUSR) != -1) | |
105 return check_fifo(name); | |
106 else | |
107 return FALSE; | |
108 } | |
109 | |
110 /* file exists */ | |
111 if (S_ISFIFO(finfo.st_mode)) | |
112 return TRUE; | |
113 else | |
114 return FALSE; | |
115 } | |
116 | |
117 static gboolean attach_fifo(const char *name) | |
118 { | |
119 GSource *source; | |
120 int fd = open (name, O_RDONLY|O_NONBLOCK); | |
121 if (fd == -1) | |
122 return FALSE; | |
123 | |
124 fifo_channel = g_io_channel_unix_new(fd); | |
125 | |
126 g_io_channel_set_flags(fifo_channel, G_IO_FLAG_NONBLOCK, NULL); | |
127 g_io_channel_set_encoding(fifo_channel, NULL, NULL); | |
128 g_io_channel_set_close_on_unref(fifo_channel, TRUE); | |
129 | |
130 source = g_io_create_watch(fifo_channel, | |
131 G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL); | |
132 g_source_set_callback(source, (GSourceFunc)fifo_callback, | |
133 (gpointer)fifo_channel, | |
134 (GDestroyNotify)fifo_destroy_callback); | |
135 g_source_attach(source, main_context); | |
136 | |
137 return TRUE; | |
138 } | |
139 | |
49 int fifo_init(const char *fifo_path) | 140 int fifo_init(const char *fifo_path) |
50 { | 141 { |
51 struct stat buf; | 142 if (fifo_path) { |
52 int fd; | 143 fifo_name = expand_filename(fifo_path); |
53 char *fifo_path_xp; | |
54 | 144 |
55 if (!sfd && !fifo_path) | 145 if (!check_fifo(fifo_name)) { |
56 return -1; // Nothing to do... | |
57 | |
58 if (sfd && !fifo_path) { // We want to reinitialize the pipe | |
59 fclose(sfd); | |
60 sfd = NULL; | |
61 if (fifo_name) | |
62 goto fifo_init_open; | |
63 } | |
64 sfd = NULL; | |
65 | |
66 fifo_path_xp = expand_filename(fifo_path); | |
67 | |
68 if (!stat(fifo_path_xp, &buf)) { | |
69 if (!S_ISFIFO(buf.st_mode)) { | |
70 scr_LogPrint(LPRINT_LOGNORM, "WARNING: Cannot create the FIFO. " | 146 scr_LogPrint(LPRINT_LOGNORM, "WARNING: Cannot create the FIFO. " |
71 "%s already exists and is not a pipe", fifo_path_xp); | 147 "%s already exists and is not a pipe", fifo_name); |
72 g_free(fifo_path_xp); | 148 g_free(fifo_name); |
73 return -1; | 149 return -1; |
74 } | 150 } |
151 } else if (fifo_name) | |
152 g_source_remove_by_user_data(fifo_channel); | |
153 else | |
154 return -1; | |
75 | 155 |
76 if (unlink(fifo_path_xp)) { | 156 if (!attach_fifo(fifo_name)) { |
77 scr_LogPrint(LPRINT_LOGNORM, "WARNING: Unable to unlink FIFO %s [%s]", | 157 scr_LogPrint(LPRINT_LOGNORM, "Error: Cannot open fifo"); |
78 fifo_path_xp, g_strerror(errno)); | |
79 g_free(fifo_path_xp); | |
80 return -1; | |
81 } | |
82 } | |
83 | |
84 if (mkfifo(fifo_path_xp, S_IWUSR | S_IRUSR)) { | |
85 scr_LogPrint(LPRINT_LOGNORM, "WARNING: Cannot create the FIFO [%s]", | |
86 g_strerror(errno)); | |
87 g_free(fifo_path_xp); | |
88 return -1; | 158 return -1; |
89 } | 159 } |
90 | 160 |
91 fifo_name = fifo_path_xp; | |
92 | |
93 fifo_init_open: | |
94 fd = open(fifo_name, O_RDONLY | O_NONBLOCK); | |
95 if (!fd) | |
96 return -1; | |
97 | |
98 setenv(FIFO_ENV_NAME, fifo_name, 1); | 161 setenv(FIFO_ENV_NAME, fifo_name, 1); |
99 | 162 |
100 sfd = fdopen(fd, "r"); | 163 scr_LogPrint(LPRINT_LOGNORM, "FIFO initialized (%s)", fifo_path); |
101 if (fifo_path) | 164 return 1; |
102 scr_LogPrint(LPRINT_LOGNORM, "FIFO initialized (%s)", fifo_name); | |
103 return 0; | |
104 } | 165 } |
105 | 166 |
106 // fifo_deinit() | |
107 // Close the current FIFO pipe and delete it. | |
108 void fifo_deinit(void) | 167 void fifo_deinit(void) |
109 { | 168 { |
110 unsetenv(FIFO_ENV_NAME); | 169 unsetenv(FIFO_ENV_NAME); |
111 if (sfd) { | |
112 fclose(sfd); | |
113 sfd = NULL; | |
114 } | |
115 if (fifo_name) { | |
116 unlink(fifo_name); | |
117 g_free(fifo_name); | |
118 fifo_name = NULL; | |
119 } | |
120 } | |
121 | 170 |
122 // fifo_read() | 171 /* destroy open fifo */ |
123 // Read a line from the FIFO pipe (if available), and execute it. | 172 unlink(fifo_name); |
124 void fifo_read(void) | 173 g_source_remove_by_user_data(fifo_channel); |
125 { | 174 /* channel itself should be destroyed by destruction callback */ |
126 struct timeval tv; | 175 g_free(fifo_name); |
127 fd_set fds; | |
128 char *getbuf; | |
129 char buf[HBB_BLOCKSIZE+1]; | |
130 int fd; | |
131 | |
132 if (!sfd) { | |
133 return; | |
134 } | |
135 | |
136 tv.tv_sec = 0; | |
137 tv.tv_usec = 0; | |
138 | |
139 fd = fileno(sfd); | |
140 | |
141 FD_ZERO(&fds); | |
142 FD_SET(fd, &fds); | |
143 | |
144 select(fd + 1, &fds, NULL, NULL, &tv); | |
145 | |
146 if (!FD_ISSET(fd, &fds)) { | |
147 return; | |
148 } | |
149 | |
150 getbuf = fgets(buf, HBB_BLOCKSIZE, sfd); | |
151 if (getbuf) { | |
152 guint logflag; | |
153 char *eol = buf; | |
154 guint fifo_ignore = settings_opt_get_int("fifo_ignore"); | |
155 | |
156 // Strip trailing newlines | |
157 for ( ; *eol ; eol++) | |
158 ; | |
159 if (eol > buf) | |
160 eol--; | |
161 while (eol > buf && *eol == '\n') | |
162 *eol-- = 0; | |
163 | |
164 if (settings_opt_get_int("fifo_hide_commands")) | |
165 logflag = LPRINT_LOG; | |
166 else | |
167 logflag = LPRINT_LOGNORM; | |
168 scr_LogPrint(logflag, "%s FIFO command: %s", | |
169 (fifo_ignore ? "Ignoring" : "Executing"), buf); | |
170 if (!fifo_ignore) { | |
171 if (process_command(buf, TRUE) == 255) | |
172 mcabber_set_terminate_ui(); | |
173 } | |
174 } else { | |
175 if (feof(sfd)) | |
176 fifo_init(NULL); // Reopen the FIFO on EOF | |
177 } | |
178 } | |
179 | |
180 // fifo_get_fd() | |
181 // Return the FIFO file descriptor (-1 if none). | |
182 int fifo_get_fd(void) | |
183 { | |
184 if (sfd) | |
185 return fileno(sfd); | |
186 return -1; | |
187 } | 176 } |
188 | 177 |
189 /* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */ | 178 /* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */ |