Vuo  2.0.0
VuoRuntime.cc
Go to the documentation of this file.
1 
10 #include <getopt.h>
11 #include <mach-o/dyld.h> // for _NSGetExecutablePath()
12 #include <libgen.h> // for dirname()
13 #include <dirent.h>
14 #include <dlfcn.h>
15 #include <sys/mman.h>
16 
17 #include "VuoApp.h"
18 #include "VuoEventLoop.h"
19 #include "VuoException.hh"
20 #include "VuoRuntime.h"
23 #include "VuoRuntimeState.hh"
24 
25 extern "C"
26 {
27 
28 static VuoRuntimeState *runtimeState = NULL;
29 void *vuoRuntimeState = NULL;
30 
34 void vuoInit(int argc, char **argv)
35 {
36  bool doAppInit = false;
37  char *controlURL = NULL;
38  char *telemetryURL = NULL;
39  bool isPaused = false;
40  pid_t runnerPid = 0;
41  int runnerPipe = -1;
42  bool continueIfRunnerDies = false;
43  bool doPrintHelp = false;
44  bool doPrintLicenses = false;
45 
46  // parse commandline arguments
47  {
48  int getoptArgC = 0;
49  char **getoptArgV = (char **)malloc(sizeof(char *) * argc);
50  for(int i = 0; i < argc; ++i)
51  {
52  // Don't pass the OS X Process Serial Number argument to getopt, since it can't handle long arguments with a single hyphen.
53  if (strncmp(argv[i], "-psn_", 5) == 0)
54  {
55  // Since we have a process serial number, we can assume this is an exported app being invoked by LaunchServices.
56  // Therefore we need to initialize the app (so it shows up in the dock).
57  doAppInit = true;
58  continue;
59  }
60 
61  getoptArgV[getoptArgC++] = argv[i];
62  }
63 
64  static struct option options[] = {
65  {"help", no_argument, NULL, 0},
66  {"vuo-control", required_argument, NULL, 0},
67  {"vuo-telemetry", required_argument, NULL, 0},
68  {"vuo-pause", no_argument, NULL, 0},
69  {"vuo-loader", required_argument, NULL, 0},
70  {"vuo-runner-pipe", required_argument, NULL, 0},
71  {"vuo-continue-if-runner-dies", no_argument, NULL, 0},
72  {"vuo-licenses", no_argument, NULL, 0},
73  {"vuo-runner-pid", required_argument, NULL, 0},
74  {NULL, no_argument, NULL, 0}
75  };
76  int optionIndex=-1;
77  int ret;
78  while ((ret = getopt_long(getoptArgC, getoptArgV, "", options, &optionIndex)) != -1)
79  {
80  if (ret == '?')
81  continue;
82 
83  switch(optionIndex)
84  {
85  case 0: // --help
86  doPrintHelp = true;
87  break;
88  case 1: // --vuo-control
89  if (controlURL)
90  free(controlURL);
91  controlURL = strdup(optarg);
92  break;
93  case 2: // --vuo-telemetry
94  if (telemetryURL)
95  free(telemetryURL);
96  telemetryURL = strdup(optarg);
97  break;
98  case 3: // --vuo-pause
99  isPaused = true;
100  break;
101  case 4: // --vuo-loader (ignored, but added here to avoid "unrecognized option" warning)
102  break;
103  case 5: // --vuo-runner-pipe
104  runnerPipe = atoi(optarg);
105  break;
106  case 6: // --vuo-continue-if-runner-dies
107  continueIfRunnerDies = true;
108  break;
109  case 7: // --vuo-licenses
110  doPrintLicenses = true;
111  break;
112  case 8: // --vuo-runner-pid
113  runnerPid = atoi(optarg);
114  break;
115  }
116  }
117  free(getoptArgV);
118  }
119 
120  // macOS 10.14 no longer provides the `-psn_` argument, so use an alternate method to detect LaunchServices.
121  // launchd is pid 1.
122  if (!doAppInit && !controlURL && getppid() == 1)
123  doAppInit = true;
124 
125  // Get the exported executable path.
126  char rawExecutablePath[PATH_MAX+1];
127  uint32_t size = sizeof(rawExecutablePath);
128  _NSGetExecutablePath(rawExecutablePath, &size);
129 
130  if (doPrintHelp)
131  {
132  printf("Usage: %s [options]\n"
133  "Options:\n"
134  " --help Display this information.\n"
135  " --vuo-licenses Display license information.\n",
136  argv[0]);
137 
138  exit(0);
139  }
140  else if (doPrintLicenses)
141  {
142  printf("This composition may include software licensed under the following terms:\n\n");
143 
144  // Derive the path of the app bundle's "Licenses" directory from its executable path.
145  char licensesPath[PATH_MAX+1];
146  licensesPath[0] = 0;
147  if (strlen(VuoApp_getVuoFrameworkPath()))
148  {
149  strncpy(licensesPath, VuoApp_getVuoFrameworkPath(), PATH_MAX);
150  strncat(licensesPath, "/Vuo.framework/Versions/" VUO_FRAMEWORK_VERSION_STRING "/Documentation/Licenses", PATH_MAX);
151  }
152  else if (strlen(VuoApp_getVuoRunnerFrameworkPath()))
153  {
154  strncpy(licensesPath, VuoApp_getVuoRunnerFrameworkPath(), PATH_MAX);
155  strncat(licensesPath, "/VuoRunner.framework/Versions/" VUO_FRAMEWORK_VERSION_STRING "/Documentation/Licenses", PATH_MAX);
156  }
157 
158  bool foundLicenses = false;
159  if (licensesPath[0] && access(licensesPath, 0) == 0)
160  {
161  DIR *dirp = opendir(licensesPath);
162  struct dirent *dp;
163  while ((dp = readdir(dirp)) != NULL)
164  {
165  if (dp->d_name[0] == '.')
166  continue;
167 
168  printf("=== %s =====================================================\n\n",dp->d_name);
169 
170  size_t pathSize = strlen(licensesPath) + dp->d_namlen + 2;
171  char licensePath[pathSize];
172  strlcpy(licensePath, licensesPath, pathSize);
173  strlcat(licensePath, "/", pathSize);
174  strlcat(licensePath, dp->d_name, pathSize);
175 
176  int fd = open(licensePath, O_RDONLY);
177  char data[1024];
178  int bytesRead;
179  while((bytesRead = read(fd, data, 1024)) > 0)
180  write(1, data, bytesRead);
181  close(fd);
182 
183  printf("\n\n\n");
184  foundLicenses = true;
185  }
186  closedir(dirp);
187  }
188 
189  if (!foundLicenses)
190  printf("(No license information found.)\n");
191 
192  free(controlURL);
193  free(telemetryURL);
194  exit(0);
195  }
196 
197  void *executableHandle = dlopen(rawExecutablePath, 0);
198  if (! executableHandle)
199  {
200  VUserLog("The composition couldn't be started because a handle to the executable couldn't be obtained : %s", dlerror());
201  free(controlURL);
202  free(telemetryURL);
203  return;
204  }
205 
207  executableHandle, NULL, doAppInit);
208 
209  dlclose(executableHandle);
210 
211  free(controlURL);
212  free(telemetryURL);
213 }
214 
233 void vuoInitInProcess(void *ZMQContext, const char *controlURL, const char *telemetryURL, bool isPaused, pid_t runnerPid,
234  int runnerPipe, bool continueIfRunnerDies, const char *workingDirectory,
235  void *compositionBinaryHandle, void *previousRuntimeState, bool doAppInit)
236 {
237  runtimeState = (VuoRuntimeState *)previousRuntimeState;
238  if (! runtimeState)
239  {
243  }
244 
245  vuoRuntimeState = (void *)runtimeState;
246 
247  VuoCompositionState *compositionState = new VuoCompositionState;
248  compositionState->runtimeState = runtimeState;
249  compositionState->compositionIdentifier = "";
251 
252  try
253  {
255  workingDirectory, compositionBinaryHandle);
256  }
257  catch (VuoException &e)
258  {
259  VUserLog("%s", e.what());
260  return;
261  }
262 
263  if (doAppInit)
264  {
265  typedef void (*vuoAppInitType)(bool requiresDockIcon);
266  vuoAppInitType vuoAppInit = (vuoAppInitType) dlsym(RTLD_SELF, "VuoApp_init");
267  if (vuoAppInit)
268  vuoAppInit(true);
269  }
270 
272 
273  // If the composition had a pending call to vuoStopComposition() when it was stopped, call it again.
276 
278 }
279 
287 void * vuoFini(void)
288 {
289  runtimeState->fini();
290  return (void *)runtimeState;
291 }
292 
297 {
298  delete (VuoRuntimeState *)runtimeState;
299 }
300 
304 typedef void (*VuoCompositionFiniCallback)(void);
305 
311 {
312  VuoRuntimeState *r = (compositionState ? (VuoRuntimeState *)compositionState->runtimeState : runtimeState);
313  if (r)
315  else
317 }
318 
323 pid_t vuoGetRunnerPid(VuoCompositionState *compositionState)
324 {
325  VuoRuntimeState *r = (compositionState ? (VuoRuntimeState *)compositionState->runtimeState : runtimeState);
326  if (r)
327  return r->getRunnerPid();
328  else
329  return -1;
330 }
331 
337 {
338  VuoRuntimeState *r = (compositionState ? (VuoRuntimeState *)compositionState->runtimeState : runtimeState);
339  if (r)
341 }
342 
348 {
349  VuoRuntimeState *r = (compositionState ? (VuoRuntimeState *)compositionState->runtimeState : runtimeState);
350  if (r)
352 }
353 
359 {
360  VuoRuntimeState *r = (compositionState ? (VuoRuntimeState *)compositionState->runtimeState : runtimeState);
361  if (r)
362  r->disableTermination();
363 }
364 
370 {
371  VuoRuntimeState *r = (compositionState ? (VuoRuntimeState *)compositionState->runtimeState : runtimeState);
372  if (r)
373  r->enableTermination();
374 }
375 
382 {
383  return runtimeState->isStopped();
384 }
385 
386 } // extern "C"