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... */