Changeset 2233f7f


Ignore:
Timestamp:
Sep 30, 2018 9:40:43 AM (2 years ago)
Author:
meeh <meeh@…>
Branches:
master
Children:
b5497ef
Parents:
36b758f
Message:

Mac OSX Launcher:

  • Update readme about event manager
  • RouterTask? can now detect a running router by scanning processes&arguments for i2p.jar
  • The logger will log to OSX's default: ~/Library/Logs?/I2P/[whatever].log
Location:
launchers/macosx
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • launchers/macosx/README.md

    r36b758f r2233f7f  
    1919| router_version | the version string | Triggered when we have successfully extracted current I2P version |
    2020| extract_errored | the error message | Triggered if the process didn't exit correctly |
     21| router_already_running | an error message | Triggered if any processes containing i2p.jar in name/arguments already exists upon router launch |
    2122
    2223## Misc
     
    3637
    3738
     39## Objective-C / Swift Links
     40
     41* https://nshipster.com/at-compiler-directives/
     42* https://developer.apple.com/documentation/swift/cocoa_design_patterns/handling_cocoa_errors_in_swift
     43* https://content.pivotal.io/blog/rails-to-ios-what-the-are-these-symbols-in-my-code
     44* https://mackuba.eu/2008/10/05/learn-objective-c-in-30-minutes/
     45* https://en.wikipedia.org/wiki/Objective-C
     46* http://cocoadevcentral.com/d/learn_objectivec/
     47
  • launchers/macosx/RouterTask.h

    r36b758f r2233f7f  
    33#include <dispatch/dispatch.h>
    44#include <memory.h>
     5#include <sys/sysctl.h>
     6#include <stdio.h>
     7#include <stdlib.h>
     8#include <string.h>
    59
    610#include <Cocoa/Cocoa.h>
     
    1014#include <vector>
    1115#include <string>
     16
     17
    1218
    1319const std::vector<NSString*> defaultStartupFlags {
     
    5359
    5460
    55 
    56 
    57 
    58 
     61@interface IIProcessInfo : NSObject {
     62 
     63@private
     64  int numberOfProcesses;
     65  NSMutableArray *processList;
     66}
     67- (id) init;
     68- (int)numberOfProcesses;
     69- (void)obtainFreshProcessList;
     70- (BOOL)findProcessWithStringInNameOrArguments:(NSString *)procNameToSearch;
     71@end
     72
     73#ifdef __cplusplus
     74
     75// Inspired by the "ps" util.
     76
     77inline std::string getArgvOfPid(int pid) {
     78  int    mib[3], argmax, nargs, c = 0;
     79  size_t    size;
     80  char    *procargs, *sp, *np, *cp;
     81  int show_args = 1;
     82  mib[0] = CTL_KERN;
     83  mib[1] = KERN_ARGMAX;
     84 
     85  size = sizeof(argmax);
     86  if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) {
     87    return std::string("sorry");
     88  }
     89 
     90  /* Allocate space for the arguments. */
     91  procargs = (char *)malloc(argmax);
     92  if (procargs == NULL) {
     93    return std::string("sorry");
     94  }
     95  /*
     96   * Make a sysctl() call to get the raw argument space of the process.
     97   * The layout is documented in start.s, which is part of the Csu
     98   * project.  In summary, it looks like:
     99   *
     100   * /---------------\ 0x00000000
     101   * :               :
     102   * :               :
     103   * |---------------|
     104   * | argc          |
     105   * |---------------|
     106   * | arg[0]        |
     107   * |---------------|
     108   * :               :
     109   * :               :
     110   * |---------------|
     111   * | arg[argc - 1] |
     112   * |---------------|
     113   * | 0             |
     114   * |---------------|
     115   * | env[0]        |
     116   * |---------------|
     117   * :               :
     118   * :               :
     119   * |---------------|
     120   * | env[n]        |
     121   * |---------------|
     122   * | 0             |
     123   * |---------------| <-- Beginning of data returned by sysctl() is here.
     124   * | argc          |
     125   * |---------------|
     126   * | exec_path     |
     127   * |:::::::::::::::|
     128   * |               |
     129   * | String area.  |
     130   * |               |
     131   * |---------------| <-- Top of stack.
     132   * :               :
     133   * :               :
     134   * \---------------/ 0xffffffff
     135   */
     136  mib[0] = CTL_KERN;
     137  mib[1] = KERN_PROCARGS2;
     138  mib[2] = pid;
     139 
     140 
     141  size = (size_t)argmax;
     142  if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) {
     143    free(procargs);
     144    return std::string("sorry");
     145  }
     146 
     147  memcpy(&nargs, procargs, sizeof(nargs));
     148  cp = procargs + sizeof(nargs);
     149 
     150  /* Skip the saved exec_path. */
     151  for (; cp < &procargs[size]; cp++) {
     152    if (*cp == '\0') {
     153      /* End of exec_path reached. */
     154      break;
     155    }
     156  }
     157  if (cp == &procargs[size]) {
     158    free(procargs);
     159    return std::string("sorry");
     160  }
     161 
     162  /* Skip trailing '\0' characters. */
     163  for (; cp < &procargs[size]; cp++) {
     164    if (*cp != '\0') {
     165      /* Beginning of first argument reached. */
     166      break;
     167    }
     168  }
     169  if (cp == &procargs[size]) {
     170    free(procargs);
     171    return std::string("sorry");
     172  }
     173  /* Save where the argv[0] string starts. */
     174  sp = cp;
     175 
     176  /*
     177   * Iterate through the '\0'-terminated strings and convert '\0' to ' '
     178   * until a string is found that has a '=' character in it (or there are
     179   * no more strings in procargs).  There is no way to deterministically
     180   * know where the command arguments end and the environment strings
     181   * start, which is why the '=' character is searched for as a heuristic.
     182   */
     183  for (np = NULL; c < nargs && cp < &procargs[size]; cp++) {
     184    if (*cp == '\0') {
     185      c++;
     186      if (np != NULL) {
     187        /* Convert previous '\0'. */
     188        *np = ' ';
     189      } else {
     190        /* *argv0len = cp - sp; */
     191      }
     192      /* Note location of current '\0'. */
     193      np = cp;
     194     
     195      if (!show_args) {
     196        /*
     197         * Don't convert '\0' characters to ' '.
     198         * However, we needed to know that the
     199         * command name was terminated, which we
     200         * now know.
     201         */
     202        break;
     203      }
     204    }
     205  }
     206 
     207  /*
     208   * sp points to the beginning of the arguments/environment string, and
     209   * np should point to the '\0' terminator for the string.
     210   */
     211  if (np == NULL || np == sp) {
     212    /* Empty or unterminated string. */
     213    free(procargs);
     214    return std::string("sorry");
     215  }
     216  return std::string(sp);
     217}
     218
     219#endif
  • launchers/macosx/RouterTask.mm

    r36b758f r2233f7f  
    99#import "I2PLauncher-Swift.h"
    1010#include "AppDelegate.h"
     11
     12#include <assert.h>
     13#include <errno.h>
     14#include <stdbool.h>
     15#include <sys/sysctl.h>
    1116#endif
    1217
     
    2126@implementation I2PRouterTask
    2227
    23 
    2428- (void)routerStdoutData:(NSNotification *)notification
    2529{
     
    3236  self.userRequestedRestart = NO;
    3337  self.isRouterRunning = NO;
    34   //self.input = [NSFileHandle fileHandleWithStandardInput];
    3538  self.routerTask = [NSTask new];
    3639  self.processPipe = [NSPipe new];
     
    4851    // Cleanup
    4952    NSLog(@"termHandler triggered!");
    50     auto swiftRouterStatus = [[RouterProcessStatus alloc] init];
    51     [swiftRouterStatus setRouterStatus: false];
    52     [swiftRouterStatus setRouterRanByUs: false];
    53     [swiftRouterStatus triggerEventWithEn:@"router_stop" details:@"normal shutdown"];
     53    [[[RouterProcessStatus alloc] init] triggerEventWithEn:@"router_stop" details:@"normal shutdown"];
    5454    [[SBridge sharedInstance] setCurrentRouterInstance:nil];
    5555    sendUserNotification(APP_IDSTR, @"I2P Router has stopped");
    5656  }];
    57     return self;
     57  return self;
    5858}
    5959
     
    8484        {
    8585                NSLog(@"Expection occurred %@", [e reason]);
    86     auto swiftRouterStatus = [[RouterProcessStatus alloc] init];
    8786    self.isRouterRunning = NO;
    88     [swiftRouterStatus triggerEventWithEn:@"router_exception" details:[e reason]];
     87    [[[RouterProcessStatus alloc] init] triggerEventWithEn:@"router_exception" details:[e reason]];
    8988    [[SBridge sharedInstance] setCurrentRouterInstance:nil];
    9089    sendUserNotification(@"An error occured, can't start the I2P Router", [e reason]);
     
    9998
    10099@end
     100
     101typedef struct kinfo_proc kinfo_proc;
     102
     103@implementation IIProcessInfo
     104- (id) init
     105{
     106  self = [super init];
     107  if (self != nil)
     108  {
     109    numberOfProcesses = -1; // means "not initialized"
     110    processList = NULL;
     111  }
     112  return self;
     113}
     114
     115- (int)numberOfProcesses
     116{
     117  return numberOfProcesses;
     118}
     119
     120- (void)setNumberOfProcesses:(int)num
     121{
     122  numberOfProcesses = num;
     123}
     124
     125- (int)getBSDProcessList:(kinfo_proc **)procList
     126   withNumberOfProcesses:(size_t *)procCount
     127{
     128#ifdef __cplusplus
     129  int             err;
     130  kinfo_proc *    result;
     131  bool            done;
     132  static const int    name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
     133  size_t          length;
     134 
     135  // a valid pointer procList holder should be passed
     136  assert( procList != NULL );
     137  // But it should not be pre-allocated
     138  assert( *procList == NULL );
     139  // a valid pointer to procCount should be passed
     140  assert( procCount != NULL );
     141 
     142  *procCount = 0;
     143  result = NULL;
     144  done = false;
     145 
     146  do
     147  {
     148    assert( result == NULL );
     149   
     150    // Call sysctl with a NULL buffer to get proper length
     151    length = 0;
     152    err = sysctl((int *)name,(sizeof(name)/sizeof(*name))-1,NULL,&length,NULL,0);
     153    if( err == -1 )
     154      err = errno;
     155   
     156    // Now, proper length is optained
     157    if( err == 0 )
     158    {
     159      result = (kinfo_proc *)malloc(length);
     160      if( result == NULL )
     161        err = ENOMEM;   // not allocated
     162    }
     163   
     164    if( err == 0 )
     165    {
     166      err = sysctl( (int *)name, (sizeof(name)/sizeof(*name))-1, result, &length, NULL, 0);
     167      if( err == -1 )
     168        err = errno;
     169     
     170      if( err == 0 )
     171        done = true;
     172      else if( err == ENOMEM )
     173      {
     174        assert( result != NULL );
     175        free( result );
     176        result = NULL;
     177        err = 0;
     178      }
     179    }
     180  }while ( err == 0 && !done );
     181 
     182  // Clean up and establish post condition
     183  if( err != 0 && result != NULL )
     184  {
     185    free(result);
     186    result = NULL;
     187  }
     188 
     189  *procList = result; // will return the result as procList
     190  if( err == 0 )
     191    *procCount = length / sizeof( kinfo_proc );
     192  assert( (err == 0) == (*procList != NULL ) );
     193  return err;
     194}
     195
     196- (void)obtainFreshProcessList
     197{
     198  int i;
     199  kinfo_proc *allProcs = 0;
     200  size_t numProcs;
     201  NSString *procName;
     202 
     203  int err =  [self getBSDProcessList:&allProcs withNumberOfProcesses:&numProcs];
     204  if( err )
     205  {
     206    numberOfProcesses = -1;
     207    processList = NULL;
     208   
     209    return;
     210  }
     211 
     212  // Construct an array for ( process name, pid, arguments concat'ed )
     213  processList = [NSMutableArray arrayWithCapacity:numProcs];
     214  for( i = 0; i < numProcs; i++ )
     215  {
     216    int pid = (int)allProcs[i].kp_proc.p_pid;
     217    procName = [NSString stringWithFormat:@"%s, pid %d, args: %s", allProcs[i].kp_proc.p_comm, pid, getArgvOfPid(pid).c_str()];
     218    [processList addObject:procName];
     219  }
     220 
     221  [self setNumberOfProcesses:(int)numProcs];
     222  free( allProcs );
     223#endif
     224}
     225
     226
     227- (BOOL)findProcessWithStringInNameOrArguments:(NSString *)procNameToSearch
     228{
     229  BOOL seenProcessThatMatch = NO;
     230  for (NSString* processInfoStr in processList) {
     231    if ([processInfoStr containsString:procNameToSearch]) {
     232      seenProcessThatMatch = YES;
     233      break;
     234    }
     235  }
     236  return seenProcessThatMatch;
     237}
     238@end
  • launchers/macosx/SBridge.mm

    r36b758f r2233f7f  
    2121#import <AppKit/AppKit.h>
    2222#import "I2PLauncher-Swift.h"
     23#include "LoggerWorker.hpp"
     24#include "Logger.h"
     25#include "logger_c.h"
    2326
    2427#include "AppDelegate.h"
     
    2932std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments, NSString* i2pBaseDir, RouterProcessStatus* routerStatus) {
    3033  @try {
    31     RTaskOptions* options = [RTaskOptions alloc];
    32     options.binPath = javaBin;
    33     options.arguments = arguments;
    34     options.i2pBaseDir = i2pBaseDir;
    35     auto instance = [[I2PRouterTask alloc] initWithOptions: options];
    36    
    37     [[SBridge sharedInstance] setCurrentRouterInstance:instance];
    38     [instance execute];
    39     sendUserNotification(APP_IDSTR, @"The I2P router is starting up.");
    40     auto pid = [instance getPID];
    41     NSLog(@"Got pid: %d", pid);
    42     if (routerStatus != nil) {
    43       [routerStatus setRouterStatus: true];
    44       [routerStatus setRouterRanByUs: true];
    45       [routerStatus triggerEventWithEn:@"router_start" details:@"normal start"];
    46       [routerStatus triggerEventWithEn:@"router_pid" details:[NSString stringWithFormat:@"%d", pid]];
     34    IIProcessInfo* processInfoObj = [[IIProcessInfo alloc] init];
     35    [processInfoObj obtainFreshProcessList];
     36    auto anyRouterLookingProcs = [processInfoObj findProcessWithStringInNameOrArguments:@"i2p.jar"];
     37    if (anyRouterLookingProcs) {
     38      auto errMessage = @"Seems i2p is already running - I've detected another process with i2p.jar in it's arguments.";
     39      NSLog(@"%@", errMessage);
     40      sendUserNotification(APP_IDSTR, errMessage);
     41      [routerStatus triggerEventWithEn:@"router_already_running" details:@"won't start - another router is running"];
     42      return std::async(std::launch::async, []{
     43        return -1;
     44      });
     45    } else {
     46      RTaskOptions* options = [RTaskOptions alloc];
     47      options.binPath = javaBin;
     48      options.arguments = arguments;
     49      options.i2pBaseDir = i2pBaseDir;
     50      auto instance = [[I2PRouterTask alloc] initWithOptions: options];
     51     
     52      [[SBridge sharedInstance] setCurrentRouterInstance:instance];
     53      [instance execute];
     54      sendUserNotification(APP_IDSTR, @"The I2P router is starting up.");
     55      auto pid = [instance getPID];
     56      NSLog(@"Got pid: %d", pid);
     57      if (routerStatus != nil) {
     58        // TODO: Merge events router_start and router_pid ?
     59        [routerStatus triggerEventWithEn:@"router_start" details:@"normal start"];
     60        [routerStatus triggerEventWithEn:@"router_pid" details:[NSString stringWithFormat:@"%d", pid]];
     61      }
     62     
     63      return std::async(std::launch::async, [&pid]{
     64        return pid;
     65      });
    4766    }
    48    
    49     return std::async(std::launch::async, [&pid]{
    50       return pid;
    51     });
    5267  }
    5368  @catch (NSException *e)
     
    93108  auto jarList = buildClassPathForObjC(basePath);
    94109  const char * classpath = jarList.c_str();
    95   NSLog(@"Classpath from ObjC = %s", classpath);
     110  MLog(0, @"Classpath from ObjC = %s", classpath);
    96111  return [[NSString alloc] initWithUTF8String:classpath];
    97112}
     
    106121 
    107122  RouterProcessStatus* routerStatus = [[RouterProcessStatus alloc] init];
     123 
     124  NSString *confDir = [NSString stringWithFormat:@"%@/Application\\ Support/i2p", NSHomeDirectory()];
     125 
    108126  try {
    109127    std::vector<NSString*> argList = {
     
    111129      @"-Xms128m",
    112130      @"-Djava.awt.headless=true",
    113       @"-Dwrapper.logfile=/tmp/router.log",
     131      [NSString stringWithFormat:@"-Dwrapper.logfile=%@/router.log", [NSString stringWithUTF8String:getDefaultLogDir().c_str()]],
    114132      @"-Dwrapper.logfile.loglevel=DEBUG",
    115       @"-Dwrapper.java.pidfile=/tmp/routerjvm.pid",
     133      [NSString stringWithFormat:@"-Dwrapper.java.pidfile=%@/router.pid", confDir],
    116134      @"-Dwrapper.console.loglevel=DEBUG"
    117135    };
  • launchers/macosx/main.mm

    r36b758f r2233f7f  
    3333#include "include/strutil.hpp"
    3434
     35#include "Logger.h"
     36#include "LoggerWorker.hpp"
     37
    3538using namespace subprocess;
    36 
    3739#endif
    3840
    3941#define debug(format, ...) CFShow([NSString stringWithFormat:format, ## __VA_ARGS__]);
     42
     43
    4044
    4145@interface AppDelegate () <NSUserNotificationCenterDelegate, NSApplicationDelegate>
     
    6973  std::string basePath(homeDir);
    7074  basePath.append("/Library/I2P");
     75 
    7176  auto jarResPath = [launcherBundle pathForResource:@"launcher" ofType:@"jar"];
    7277  NSLog(@"Trying to load launcher.jar from url = %@", jarResPath);
     
    196201
    197202  NSBundle *launcherBundle = [NSBundle mainBundle];
     203 
    198204 
    199205  // Helper object to hold statefull path information
     
    289295@end
    290296
    291 
     297#ifdef __cplusplus
     298namespace {
     299  const std::string logDirectory = getDefaultLogDir();
     300}
     301#endif
    292302
    293303int main(int argc, const char **argv)
     
    295305  NSApplication *app = [NSApplication sharedApplication];
    296306
     307#ifdef __cplusplus
     308  mkdir(logDirectory.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
     309 
     310  SharedLogWorker logger("I2PLauncher", logDirectory);
     311  MeehLog::initializeLogging(&logger);
     312 
     313  MLOG(INFO) << "Application is starting up";
     314#endif
     315 
    297316  AppDelegate *appDelegate = [[AppDelegate alloc] initWithArgc:argc argv:argv];
    298317  app.delegate = appDelegate;
     
    305324    [NSApp terminate:nil];
    306325  }
     326#pragma GCC diagnostic push
     327#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
    307328  [NSBundle loadNibNamed:@"I2Launcher" owner:NSApp];
    308 
     329#pragma GCC diagnostic pop
     330 
    309331  [NSApp run];
    310332  return 0;
Note: See TracChangeset for help on using the changeset viewer.