1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 2013 Licq Developers <licq-dev@googlegroups.com>
4  *
5  * Please refer to the COPYRIGHT file distributed with this source
6  * distribution for the names of the individual contributors.
7  *
8  * Licq is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * Licq is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with Licq; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22 
23 #include "plugininstance.h"
24 
25 #include <licq/plugin/plugininterface.h>
26 
27 #include <cassert>
28 #include <cstring>
29 
30 // From licq.cpp
31 extern char** global_argv;
32 
33 using namespace LicqDaemon;
34 
PluginInstance(int id,PluginThread::Ptr thread)35 PluginInstance::PluginInstance(
36     int id, PluginThread::Ptr thread)
37   : myId(id),
38     myThread(thread),
39     myIsRunning(false),
40     myArgc(0),
41     myArgv(NULL),
42     myArgvCopy(NULL),
43     myCreateCallback(NULL),
44     myStartCallback(NULL),
45     myExitCallback(NULL)
46 {
47   // Empty
48 }
49 
~PluginInstance()50 PluginInstance::~PluginInstance()
51 {
52   for (int i = 0; i < myArgc; ++i)
53     ::free(myArgv[i]);
54   delete[] myArgv;
55   delete[] myArgvCopy;
56 }
57 
id() const58 int PluginInstance::id() const
59 {
60   return myId;
61 }
62 
internalInterface()63 boost::shared_ptr<Licq::PluginInterface> PluginInstance::internalInterface()
64 {
65   // Create a shared_ptr that keeps this object alive at least until the
66   // returned pointer goes out of scope.
67   return boost::shared_ptr<Licq::PluginInterface>(
68       shared_from_this(), interface().get());
69 }
70 
isThread(const pthread_t & thread) const71 bool PluginInstance::isThread(const pthread_t& thread) const
72 {
73   return myThread->isThread(thread);
74 }
75 
create(void (* callback)(const PluginInstance &))76 bool PluginInstance::create(void (*callback)(const PluginInstance&))
77 {
78   assert(myCreateCallback == NULL);
79   myCreateCallback = callback;
80 
81   myThread->createPlugin(&createThreadEntry, this);
82   return !! interface();
83 }
84 
init(int argc,char ** argv)85 bool PluginInstance::init(int argc, char** argv)
86 {
87   const size_t size = argc + 2;
88 
89   myArgv = new char*[size];
90   myArgvCopy = new char*[size];
91 
92   myArgv[size - 1] = NULL;
93 
94   myArgv[0] = ::strdup(global_argv[0]);
95 
96   for (int i = 0; i < argc; ++i)
97     myArgv[i + 1] = ::strdup(argv[i]);
98 
99   myArgc = argc + 1;
100 
101   // We need to create a copy of myArgv and pass that to the plugin, since
102   // e.g. KDE changes the pointers in argv (e.g. to strip the path in argv[0])
103   // and that messes up free, causing SIGSEGV in the destructor.
104   ::memcpy(myArgvCopy, myArgv, size * sizeof(char*));
105 
106   return myThread->initPlugin(&initThreadEntry, this);
107 }
108 
run(void (* startCallback)(const PluginInstance &),void (* exitCallback)(const PluginInstance &))109 void PluginInstance::run(void (*startCallback)(const PluginInstance&),
110                          void (*exitCallback)(const PluginInstance&))
111 {
112   assert(myStartCallback == NULL && myExitCallback == NULL);
113   myStartCallback = startCallback;
114   myExitCallback = exitCallback;
115 
116   myThread->startPlugin(&startThreadEntry, this);
117 }
118 
shutdown()119 void PluginInstance::shutdown()
120 {
121   interface()->shutdown();
122   myIsRunning = false;
123 }
124 
joinThread()125 int PluginInstance::joinThread()
126 {
127   void* result = myThread->join();
128   if (result != NULL && result != PTHREAD_CANCELED)
129   {
130     int* retval = static_cast<int*>(result);
131     int value = *retval;
132     delete retval;
133     return value;
134   }
135 
136   return -1;
137 }
138 
cancelThread()139 void PluginInstance::cancelThread()
140 {
141   myThread->cancel();
142 }
143 
createThreadEntry(void * plugin)144 void PluginInstance::createThreadEntry(void* plugin)
145 {
146   PluginInstance* thisPlugin = static_cast<PluginInstance*>(plugin);
147 
148   if (thisPlugin->myCreateCallback)
149     thisPlugin->myCreateCallback(*thisPlugin);
150 
151   thisPlugin->createInterface();
152 }
153 
initThreadEntry(void * plugin)154 bool PluginInstance::initThreadEntry(void* plugin)
155 {
156   PluginInstance* thisPlugin = static_cast<PluginInstance*>(plugin);
157 
158   // Set optind to 0 so plugins can use getopt
159   optind = 0;
160 
161   return thisPlugin->interface()->init(
162       thisPlugin->myArgc, thisPlugin->myArgvCopy);
163 }
164 
startThreadEntry(void * plugin)165 void* PluginInstance::startThreadEntry(void* plugin)
166 {
167   PluginInstance* thisPlugin = static_cast<PluginInstance*>(plugin);
168 
169   if (thisPlugin->myStartCallback != NULL)
170     (*thisPlugin->myStartCallback)(*thisPlugin);
171 
172   thisPlugin->myIsRunning = true;
173 
174   int* retval = new int;
175   *retval = thisPlugin->interface()->run();
176 
177   if (thisPlugin->myExitCallback != NULL)
178     (*thisPlugin->myExitCallback)(*thisPlugin);
179 
180   return retval;
181 }
182