【脱獄アプリ開発】第9回SyslogをON/OFFできるFlipSwitchトグルの作成方法

Jailbreak Tweakを開発するときなどにデバイスの情報をログとして出力する「syslogd to var/log/syslog」があります。

過去にSBSettingsのトグルとしてもありましたが、今回はそれと同様の動作をするFlipSwitchトグルの作成方法を紹介します。

syslogd to var/log/syslogの使い方

名前の通り/var/log/syslogにログファイルをリアルタイムで生成します。

OpenSSHとBigBoss Recommended Toolsをインストールしていれば、MacのターミナルなどからSSHでiPhoneに接続しリアルタイムでログを確認できます。
#SSHでiPhoneに接続
ssh [email protected]

#tailコマンドでログを確認
tail -f /var/log/syslog

#NSLogなどで一部の名称のみ確認したい場合(例.hoge)
tail -f /var/log/syslog | grep hoge
Tweak作成時などで上手いこと行かない場合、ソースファイルにNSLog()を記述して調べてみると解決の糸口に繋がるかと思います。

TheosにFlipSwitchのテンプレートを入れる

Link:https://github.com/a3tweaks/Flipswitch
NIC Templateの中にある「iphone_flipswitch_switch.nic.tar」を、Macの"/opt/theos/templates/iphone"ディレクトリに入れます。そうすると、Theosのパッケージを作成する項目にFlipSwitchが追加されます。
Theosの構築などについては過去記事を参照ください。

【脱獄アプリ開発】第7回theosのインストールから簡単なTweakの作成までのガイドライン | Will feel Tips
【脱獄アプリ開発】第8回armv6からarm64(iPhone 3Gから5s)に対応したTweakの作成 | Will feel Tips

Syslog Toggleに必要な動作

1.syslogdのデーモンをON/OFFする
launchctlコマンドで"/System/Library/LaunchDaemons/com.apple.syslogd.plist"をunload/loadしてあげることで可能となります。
ただし、FlipSwitchではSBSettingsのようにroot権限で動作することが出来ないため、Theosでいう"tool"を使ったバイナリを別に作成します。以下は、main.mmに記述していきます。
#import <Foundation/NSTask.h>
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath: @"/bin/launchctl"];
//unload
NSArray *unload = [NSArray arrayWithObjects: @"unload", @"/System/Library/LaunchDaemons/com.apple.syslogd.plist", nil];
[task setArguments:unload];
[task launch];

//load
NSArray *load = [NSArray arrayWithObjects: @"load", @"/System/Library/LaunchDaemons/com.apple.syslogd.plist", nil];
[task setArguments:load];
[task launch];
このようにNSTaskを利用して実行することが出来ます。

2."/etc/syslog.conf"ファイルを編集する
/var/log/syslogに出力するONの時は、"*.* /var/log/syslog"
OFFの時は、"#*.* /var/log/syslog"先頭をコメントアウトしています。

static BOOL syslogEnabled;

NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
NSData *data = [fileHandle readDataToEndOfFile];
NSString *str = [[NSString alloc]initWithData:data
encoding:NSUTF8StringEncoding];
syslogEnabled = [str hasPrefix:@"*.* /var/log/syslog"];
[fileHandle closeFile];
hasPrefix:@"*.* /var/log/syslog"で先頭の文字に#がない場合、syslogEnabledの値がYESになります。

これを利用してファイルを編集します。

static NSString *filePath = @"/etc/syslog.conf";
//OFFにするときの文字列
NSString *str1 = @"#*.* /var/log/syslog\n";
//ONにするときの文字列
NSString *str2 = @"*.* /var/log/syslog\n";
NSFileManager *manager = [NSFileManager defaultManager];
//一度"/etc/syslog.conf"を削除
if ([manager removeItemAtPath:filePath error:nil]) {
//syslog.confを新規作成
[manager createFileAtPath:filePath contents:nil attributes:nil];
if (syslogEnabled) {
//syslogがONの時OFFにする
[str1 writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
[task setArguments:unload];
[task launch];

[task setArguments:load];
[task launch];
} else {
//OFFの時ONにする
[str2 writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
[task setArguments:load];
[task launch];
}
}
以上のような方法でバイナリを作成します。

通常toolで作成した場合、"/usr/bin"ディレクトリに入りますが、FlipSwitchのバンドルに入れて見た目をスッキリさせます。名前を「syslogsw」としてFlipSwitch側を「SyslogToggle.bundle」とします。
TOOL_NAME = syslogsw
syslogsw_FILES = main.mm
syslogsw_INSTALL_PATH = /Library/Switches/SyslogToggle.bundle
こうすると「SyslogToggle.bundle」の中に「syslogsw」が入ります。
また、root権限で実行させるようにするためパーミッションを「4755」にします。

before-package::
sudo chmod 4755 $(THEOS_STAGING_DIR)/Library/Switches/SyslogToggle.bundle/syslogsw
これでdebパッケージにする前にパーミッションを変更します。

3.FlipSwitchでのON/OFF
ソースはSwitch.xファイルを見てコンパイルしますが、Xcodeで編集した場合にシンタックスハイライトされないためシンボリックリンクを作って編集すると見やすくなります。

mv Switch.x Switch.mm; ln -s Switch.mm Switch.x
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "FSSwitchDataSource.h"
#import "FSSwitchPanel.h"
static NSString *logPath = @"/var/log/syslog";
static NSString *filePath = @"/etc/syslog.conf";
static BOOL syslogEnabled;

@interface SyslogToggleSwitch : NSObject <FSSwitchDataSource>
@end

@implementation SyslogToggleSwitch
- (id)init
{
if ((self = [super init])) {
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
NSData *data = [fileHandle readDataToEndOfFile];
NSString *str = [[NSString alloc]initWithData:data
encoding:NSUTF8StringEncoding];
syslogEnabled = [str hasPrefix:@"*.* /var/log/syslog"];
[fileHandle closeFile];
}
return self;
}

- (FSSwitchState)stateForSwitchIdentifier:(NSString *)switchIdentifier
{
return syslogEnabled;
}

- (void)applyState:(FSSwitchState)newState forSwitchIdentifier:(NSString *)switchIdentifier
{
if (newState == FSSwitchStateIndeterminate)
return;

syslogEnabled = newState;

system("/Library/Switches/SyslogToggle.bundle/syslogsw");
}
メインは、"syslogsw"を実行させることになります。

4.スイッチ長押し時にログファイルサイズを表示/削除させる
//スイッチ長押しで実行する処理
- (void)applyAlternateActionForSwitchIdentifier:(NSString *)switchIdentifier
{
//syslog(logPath)のファイルサイズを取得
NSFileManager *fm = [NSFileManager defaultManager];
NSDictionary *attribute = [fm attributesOfItemAtPath:logPath error:nil];
NSNumber *fileSize = [attribute objectForKey:NSFileSize];
float num = [fileSize floatValue];
//KBに変換
num /= 1028;
//floatをNSStringに変換し、@"%.0f KB",roundf(num)で小数点を消し四捨五入
NSString *logSize = [[NSString alloc] initWithFormat:@"%.0f KB",roundf(num)];
//アラートを表示させる
UIAlertView *alert =
[[UIAlertView alloc] initWithTitle:@"Logfile Size\nLocation: /var/log/syslog"
message:logSize
delegate:self
cancelButtonTitle:@"Dismiss"
otherButtonTitles:@"Clear File",nil];
[alert show];
[alert release];

}

-(void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) {//Dismiss
} else if (buttonIndex == 1) {//Clear Fileを押した時
///var/mobile以下はFlipSwitchからアクセス出来るので空ファイルを作成
system("touch /var/mobile/Library/Caches/syslogclear");

//syslogを削除した場合、再度ONにする必要があるためOFFにする
system("/Library/Switches/SyslogToggle.bundle/syslogsw");
syslogEnabled = NO;
}
}
@end
static NSString *clearCache = @"/var/mobile/Library/Caches/syslogclear";
static NSString *logPath = @"/var/log/syslog";
//syslogclearファイルが合った場合、OFFにする記述を追加
NSString *str1 = @"#*.* /var/log/syslog\n";
NSFileManager *manager = [NSFileManager defaultManager];
if ([manager removeItemAtPath:clearCache error:nil]) {
//syslog削除
[manager removeItemAtPath:logPath error:nil];
//syslogをOFFにする
[str1 writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
[task setArguments:unload];
[task launch];

[task setArguments:load];
[task launch];
}
5.初回起動時に動作しない問題を修正
#!/bin/sh

/Library/Switches/SyslogToggle.bundle/syslogsw
exit 0
インストール後に一度"syslogsw"を実行することで回避します。

まとめ

ずらずらとコードを書きましたが、断片的で分かりにくいかと思います。GitHubにソースを上げておいたので参考にしてください。

Link:https://github.com/ichitaso/Syslog-Toggle-Flipswitch
ichitasoリポにも「Syslog Toggle(Flipswitch)」という名前で入れておきました。Cydiaの設定がHacker以上で表示されるようになっています。
昨夜はWWDC 2014でiOS 8や新しい言語「Swift」が発表されましたね!脱獄関連の動きも気になるところですが、とりあえず今できることをやろうということで紹介しました。

それでは!!