mirror of
https://github.com/weechat/weechat.git
synced 2026-07-02 15:53:12 +02:00
cf6aca1619
This pointer is the first argument received by callbacks, and the existing argument "data" is now automatically freed by WeeChat when the object containing the callback is removed. With this new pointer, the linked list of callbacks in scripts has been removed. This will improve speed of scripts (using a lot of hooks), reduce memory used by scripts and reduce time to unload scripts. Following functions are affected in the C API: * exec_on_files * config_new * config_new_section * config_new_option * hook_command * hook_command_run * hook_timer * hook_fd * hook_process * hook_process_hashtable * hook_connect * hook_print * hook_signal * hook_hsignal * hook_config * hook_completion * hook_modifier * hook_info * hook_info_hashtable * hook_infolist * hook_hdata * hook_focus * unhook_all_plugin * buffer_new * bar_item_new * upgrade_new * upgrade_read
472 lines
12 KiB
C
472 lines
12 KiB
C
/*
|
|
* fifo.c - fifo plugin for WeeChat: remote control with FIFO pipe
|
|
*
|
|
* Copyright (C) 2003-2016 Sébastien Helleu <flashcode@flashtux.org>
|
|
*
|
|
* This file is part of WeeChat, the extensible chat client.
|
|
*
|
|
* WeeChat is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* WeeChat is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with WeeChat. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <dirent.h>
|
|
#include <limits.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
|
|
#include "../weechat-plugin.h"
|
|
#include "fifo.h"
|
|
#include "fifo-command.h"
|
|
#include "fifo-info.h"
|
|
|
|
|
|
WEECHAT_PLUGIN_NAME(FIFO_PLUGIN_NAME);
|
|
WEECHAT_PLUGIN_DESCRIPTION(N_("FIFO pipe for remote control"));
|
|
WEECHAT_PLUGIN_AUTHOR("Sébastien Helleu <flashcode@flashtux.org>");
|
|
WEECHAT_PLUGIN_VERSION(WEECHAT_VERSION);
|
|
WEECHAT_PLUGIN_LICENSE(WEECHAT_LICENSE);
|
|
WEECHAT_PLUGIN_PRIORITY(7000);
|
|
|
|
#define FIFO_FILENAME_PREFIX "weechat_fifo_"
|
|
|
|
struct t_weechat_plugin *weechat_fifo_plugin = NULL;
|
|
#define weechat_plugin weechat_fifo_plugin
|
|
|
|
int fifo_quiet = 0;
|
|
int fifo_fd = -1;
|
|
struct t_hook *fifo_fd_hook = NULL;
|
|
char *fifo_filename;
|
|
char *fifo_unterminated = NULL;
|
|
|
|
|
|
int fifo_fd_cb ();
|
|
|
|
|
|
/*
|
|
* Removes old FIFO pipes in directory.
|
|
*/
|
|
|
|
void
|
|
fifo_remove_old_pipes ()
|
|
{
|
|
char *buf;
|
|
int buf_len, prefix_len;
|
|
const char *weechat_home, *dir_separator;
|
|
DIR *dp;
|
|
struct dirent *entry;
|
|
struct stat statbuf;
|
|
|
|
buf_len = PATH_MAX;
|
|
buf = malloc (buf_len);
|
|
if (!buf)
|
|
return;
|
|
|
|
weechat_home = weechat_info_get ("weechat_dir", "");
|
|
dir_separator = weechat_info_get ("dir_separator", "");
|
|
|
|
prefix_len = strlen (FIFO_FILENAME_PREFIX);
|
|
|
|
dp = opendir (weechat_home);
|
|
if (dp != NULL)
|
|
{
|
|
while ((entry = readdir (dp)) != NULL)
|
|
{
|
|
if (strcmp (entry->d_name, ".") == 0 || strcmp (entry->d_name, "..") == 0)
|
|
continue;
|
|
|
|
if (strncmp (entry->d_name, FIFO_FILENAME_PREFIX, prefix_len) == 0)
|
|
{
|
|
snprintf (buf, buf_len, "%s%s%s",
|
|
weechat_home, dir_separator, entry->d_name);
|
|
if (stat (buf, &statbuf) != -1)
|
|
{
|
|
weechat_printf (NULL,
|
|
_("%s: removing old fifo pipe \"%s\""),
|
|
FIFO_PLUGIN_NAME, buf);
|
|
unlink (buf);
|
|
}
|
|
}
|
|
}
|
|
closedir (dp);
|
|
}
|
|
|
|
free (buf);
|
|
}
|
|
|
|
/*
|
|
* Creates FIFO pipe for remote control.
|
|
*/
|
|
|
|
void
|
|
fifo_create ()
|
|
{
|
|
int filename_length;
|
|
const char *fifo_option, *weechat_home;
|
|
|
|
fifo_option = weechat_config_get_plugin ("fifo");
|
|
if (!fifo_option)
|
|
{
|
|
weechat_config_set_plugin ("fifo", "on");
|
|
fifo_option = weechat_config_get_plugin ("fifo");
|
|
}
|
|
|
|
weechat_home = weechat_info_get ("weechat_dir", "");
|
|
|
|
if (fifo_option && weechat_home)
|
|
{
|
|
fifo_remove_old_pipes ();
|
|
|
|
if (weechat_strcasecmp (fifo_option, "on") == 0)
|
|
{
|
|
/*
|
|
* build FIFO filename:
|
|
* "<weechat_home>/weechat_fifo_" + process PID
|
|
*/
|
|
if (!fifo_filename)
|
|
{
|
|
filename_length = strlen (weechat_home) + 64;
|
|
fifo_filename = malloc (filename_length);
|
|
snprintf (fifo_filename, filename_length,
|
|
"%s/%s%d",
|
|
weechat_home, FIFO_FILENAME_PREFIX, (int) getpid());
|
|
}
|
|
|
|
fifo_fd = -1;
|
|
|
|
/* create FIFO pipe, writable for user only */
|
|
if (mkfifo (fifo_filename, 0600) == 0)
|
|
{
|
|
/* open FIFO pipe in read-only, non-blocking mode */
|
|
if ((fifo_fd = open (fifo_filename,
|
|
O_RDONLY | O_NONBLOCK)) != -1)
|
|
{
|
|
if ((weechat_fifo_plugin->debug >= 1) || !fifo_quiet)
|
|
{
|
|
weechat_printf (NULL,
|
|
_("%s: pipe opened (file: %s)"),
|
|
FIFO_PLUGIN_NAME,
|
|
fifo_filename);
|
|
}
|
|
fifo_fd_hook = weechat_hook_fd (fifo_fd, 1, 0, 0,
|
|
&fifo_fd_cb, NULL, NULL);
|
|
}
|
|
else
|
|
weechat_printf (NULL,
|
|
_("%s%s: unable to open pipe (%s) for "
|
|
"reading"),
|
|
weechat_prefix ("error"), FIFO_PLUGIN_NAME,
|
|
fifo_filename);
|
|
}
|
|
else
|
|
{
|
|
weechat_printf (NULL,
|
|
_("%s%s: unable to create pipe for remote "
|
|
"control (%s): error %d %s"),
|
|
weechat_prefix ("error"), FIFO_PLUGIN_NAME,
|
|
fifo_filename, errno, strerror (errno));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Removes FIFO pipe.
|
|
*/
|
|
|
|
void
|
|
fifo_remove ()
|
|
{
|
|
/* remove fd hook */
|
|
if (fifo_fd_hook)
|
|
{
|
|
weechat_unhook (fifo_fd_hook);
|
|
fifo_fd_hook = NULL;
|
|
}
|
|
|
|
/* close FIFO pipe */
|
|
if (fifo_fd != -1)
|
|
{
|
|
close (fifo_fd);
|
|
fifo_fd = -1;
|
|
}
|
|
|
|
/* remove FIFO from disk */
|
|
if (fifo_filename)
|
|
unlink (fifo_filename);
|
|
|
|
/* remove any unterminated message */
|
|
if (fifo_unterminated)
|
|
{
|
|
free (fifo_unterminated);
|
|
fifo_unterminated = NULL;
|
|
}
|
|
|
|
/* free filename */
|
|
if (fifo_filename)
|
|
{
|
|
free (fifo_filename);
|
|
fifo_filename = NULL;
|
|
}
|
|
|
|
weechat_printf (NULL,
|
|
_("%s: pipe closed"),
|
|
FIFO_PLUGIN_NAME);
|
|
}
|
|
|
|
/*
|
|
* Executes a command/text received in FIFO pipe.
|
|
*/
|
|
|
|
void
|
|
fifo_exec (const char *text)
|
|
{
|
|
char *text2, *pos_msg;
|
|
struct t_gui_buffer *ptr_buffer;
|
|
|
|
text2 = strdup (text);
|
|
if (!text2)
|
|
return;
|
|
|
|
pos_msg = NULL;
|
|
ptr_buffer = NULL;
|
|
|
|
/*
|
|
* look for plugin + buffer name at beginning of text
|
|
* text may be: "plugin.buffer *text" or "*text"
|
|
*/
|
|
if (text2[0] == '*')
|
|
{
|
|
pos_msg = text2 + 1;
|
|
ptr_buffer = weechat_current_buffer ();
|
|
}
|
|
else
|
|
{
|
|
pos_msg = strstr (text2, " *");
|
|
if (!pos_msg)
|
|
{
|
|
weechat_printf (NULL,
|
|
_("%s%s: invalid text received in pipe"),
|
|
weechat_prefix ("error"), FIFO_PLUGIN_NAME);
|
|
free (text2);
|
|
return;
|
|
}
|
|
pos_msg[0] = '\0';
|
|
pos_msg += 2;
|
|
ptr_buffer = weechat_buffer_search ("==", text2);
|
|
if (!ptr_buffer)
|
|
{
|
|
weechat_printf (NULL,
|
|
_("%s%s: buffer \"%s\" not found"),
|
|
weechat_prefix ("error"), FIFO_PLUGIN_NAME,
|
|
text2);
|
|
free (text2);
|
|
return;
|
|
}
|
|
}
|
|
|
|
weechat_command (ptr_buffer, pos_msg);
|
|
|
|
free (text2);
|
|
}
|
|
|
|
/*
|
|
* Reads data in FIFO pipe.
|
|
*/
|
|
|
|
int
|
|
fifo_fd_cb (const void *pointer, void *data, int fd)
|
|
{
|
|
static char buffer[4096 + 2];
|
|
char *buf2, *pos, *ptr_buf, *next_ptr_buf;
|
|
int num_read;
|
|
|
|
/* make C compiler happy */
|
|
(void) pointer;
|
|
(void) data;
|
|
(void) fd;
|
|
|
|
num_read = read (fifo_fd, buffer, sizeof (buffer) - 2);
|
|
if (num_read > 0)
|
|
{
|
|
buffer[num_read] = '\0';
|
|
|
|
buf2 = NULL;
|
|
ptr_buf = buffer;
|
|
if (fifo_unterminated)
|
|
{
|
|
buf2 = malloc (strlen (fifo_unterminated) +
|
|
strlen (buffer) + 1);
|
|
if (buf2)
|
|
{
|
|
strcpy (buf2, fifo_unterminated);
|
|
strcat (buf2, buffer);
|
|
}
|
|
ptr_buf = buf2;
|
|
free (fifo_unterminated);
|
|
fifo_unterminated = NULL;
|
|
}
|
|
|
|
while (ptr_buf && ptr_buf[0])
|
|
{
|
|
next_ptr_buf = NULL;
|
|
pos = strstr (ptr_buf, "\r\n");
|
|
if (pos)
|
|
{
|
|
pos[0] = '\0';
|
|
next_ptr_buf = pos + 2;
|
|
}
|
|
else
|
|
{
|
|
pos = strstr (ptr_buf, "\n");
|
|
if (pos)
|
|
{
|
|
pos[0] = '\0';
|
|
next_ptr_buf = pos + 1;
|
|
}
|
|
else
|
|
{
|
|
fifo_unterminated = strdup (ptr_buf);
|
|
ptr_buf = NULL;
|
|
next_ptr_buf = NULL;
|
|
}
|
|
}
|
|
|
|
if (ptr_buf)
|
|
fifo_exec (ptr_buf);
|
|
|
|
ptr_buf = next_ptr_buf;
|
|
}
|
|
|
|
if (buf2)
|
|
free (buf2);
|
|
}
|
|
else
|
|
{
|
|
if (num_read < 0)
|
|
{
|
|
#ifdef __CYGWIN__
|
|
if ((errno == EAGAIN) || (errno == ECOMM))
|
|
#else
|
|
if (errno == EAGAIN)
|
|
#endif /* __CYGWIN__ */
|
|
return WEECHAT_RC_OK;
|
|
|
|
weechat_printf (NULL,
|
|
_("%s%s: error reading pipe (%d %s), closing it"),
|
|
weechat_prefix ("error"), FIFO_PLUGIN_NAME,
|
|
errno, strerror (errno));
|
|
fifo_remove ();
|
|
}
|
|
else
|
|
{
|
|
weechat_unhook (fifo_fd_hook);
|
|
fifo_fd_hook = NULL;
|
|
close (fifo_fd);
|
|
fifo_fd = open (fifo_filename, O_RDONLY | O_NONBLOCK);
|
|
if (fifo_fd < 0)
|
|
{
|
|
weechat_printf (NULL,
|
|
_("%s%s: error opening file, closing it"),
|
|
weechat_prefix ("error"), FIFO_PLUGIN_NAME);
|
|
fifo_remove ();
|
|
}
|
|
else
|
|
{
|
|
fifo_fd_hook = weechat_hook_fd (fifo_fd, 1, 0, 0,
|
|
&fifo_fd_cb, NULL, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
return WEECHAT_RC_OK;
|
|
}
|
|
|
|
/*
|
|
* Callback for changes on option "plugins.var.fifo.fifo".
|
|
*/
|
|
|
|
int
|
|
fifo_config_cb (const void *pointer, void *data,
|
|
const char *option, const char *value)
|
|
{
|
|
/* make C compiler happy */
|
|
(void) pointer;
|
|
(void) data;
|
|
(void) option;
|
|
|
|
if (weechat_strcasecmp (value, "on") == 0)
|
|
{
|
|
if (fifo_fd < 0)
|
|
fifo_create ();
|
|
}
|
|
else
|
|
{
|
|
if (fifo_fd >= 0)
|
|
fifo_remove ();
|
|
}
|
|
|
|
return WEECHAT_RC_OK;
|
|
}
|
|
|
|
/*
|
|
* Initializes fifo plugin.
|
|
*/
|
|
|
|
int
|
|
weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[])
|
|
{
|
|
char str_option[256];
|
|
|
|
/* make C compiler happy */
|
|
(void) argc;
|
|
(void) argv;
|
|
|
|
weechat_plugin = plugin;
|
|
|
|
fifo_quiet = 1;
|
|
|
|
fifo_create ();
|
|
|
|
snprintf (str_option, sizeof (str_option),
|
|
"plugins.var.fifo.%s", FIFO_OPTION_NAME);
|
|
weechat_hook_config (str_option, &fifo_config_cb, NULL, NULL);
|
|
|
|
fifo_command_init ();
|
|
fifo_info_init ();
|
|
|
|
fifo_quiet = 0;
|
|
|
|
return WEECHAT_RC_OK;
|
|
}
|
|
|
|
/*
|
|
* Ends fifo plugin.
|
|
*/
|
|
|
|
int
|
|
weechat_plugin_end (struct t_weechat_plugin *plugin)
|
|
{
|
|
/* make C compiler happy */
|
|
(void) plugin;
|
|
|
|
fifo_remove ();
|
|
|
|
return WEECHAT_RC_OK;
|
|
}
|