Skip to content

Commit 45e12b0

Browse files
First version
1 parent 4e71010 commit 45e12b0

6 files changed

Lines changed: 221 additions & 7 deletions

File tree

DeviceController.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@interface DeviceController : NSObject
4+
5+
- (BOOL) RebootDevice;
6+
7+
@end

DeviceController.m

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
#import "DeviceController.h"
2+
#include <spawn.h>
3+
#import <sys/sysctl.h>
4+
#import <Foundation/Foundation.h>
5+
#import <FrontBoardServices/FBSSystemService.h>
6+
7+
@implementation DeviceController
8+
9+
#define POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE 1
10+
extern int posix_spawnattr_set_persona_np(const posix_spawnattr_t* __restrict, uid_t, uint32_t);
11+
extern int posix_spawnattr_set_persona_uid_np(const posix_spawnattr_t* __restrict, uid_t);
12+
extern int posix_spawnattr_set_persona_gid_np(const posix_spawnattr_t* __restrict, uid_t);
13+
14+
- (BOOL) RebootDevice
15+
{
16+
NSString *path = [[NSBundle mainBundle] pathForResource:@"RebootRootHelper" ofType:@""];
17+
18+
NSArray *args = @[]; // 不需要任何额外参数
19+
NSString *stdOut = nil;
20+
NSString *stdErr = nil;
21+
22+
if (path == nil) {
23+
return NO;
24+
}
25+
26+
int result = spawnRoot(path, args, &stdOut, &stdErr);
27+
if (result == 0) {
28+
return YES;
29+
}
30+
31+
return NO;
32+
}
33+
34+
// @See https://github.com/opa334/TrollStore/blob/main/Shared/TSUtil.m#L79
35+
int spawnRoot(NSString* path, NSArray* args, NSString** stdOut, NSString** stdErr)
36+
{
37+
NSMutableArray* argsM = args.mutableCopy ?: [NSMutableArray new];
38+
[argsM insertObject:path atIndex:0];
39+
40+
NSUInteger argCount = [argsM count];
41+
char **argsC = (char **)malloc((argCount + 1) * sizeof(char*));
42+
43+
for (NSUInteger i = 0; i < argCount; i++)
44+
{
45+
argsC[i] = strdup([[argsM objectAtIndex:i] UTF8String]);
46+
}
47+
argsC[argCount] = NULL;
48+
49+
posix_spawnattr_t attr;
50+
posix_spawnattr_init(&attr);
51+
52+
posix_spawnattr_set_persona_np(&attr, 99, POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE);
53+
posix_spawnattr_set_persona_uid_np(&attr, 0);
54+
posix_spawnattr_set_persona_gid_np(&attr, 0);
55+
56+
posix_spawn_file_actions_t action;
57+
posix_spawn_file_actions_init(&action);
58+
59+
int outErr[2];
60+
if(stdErr)
61+
{
62+
pipe(outErr);
63+
posix_spawn_file_actions_adddup2(&action, outErr[1], STDERR_FILENO);
64+
posix_spawn_file_actions_addclose(&action, outErr[0]);
65+
}
66+
67+
int out[2];
68+
if(stdOut)
69+
{
70+
pipe(out);
71+
posix_spawn_file_actions_adddup2(&action, out[1], STDOUT_FILENO);
72+
posix_spawn_file_actions_addclose(&action, out[0]);
73+
}
74+
75+
pid_t task_pid;
76+
int status = -200;
77+
int spawnError = posix_spawn(&task_pid, [path UTF8String], &action, &attr, (char* const*)argsC, NULL);
78+
posix_spawnattr_destroy(&attr);
79+
for (NSUInteger i = 0; i < argCount; i++)
80+
{
81+
free(argsC[i]);
82+
}
83+
free(argsC);
84+
85+
if(spawnError != 0)
86+
{
87+
NSLog(@"posix_spawn error %d\n", spawnError);
88+
return spawnError;
89+
}
90+
91+
__block volatile BOOL _isRunning = YES;
92+
NSMutableString* outString = [NSMutableString new];
93+
NSMutableString* errString = [NSMutableString new];
94+
dispatch_semaphore_t sema = 0;
95+
dispatch_queue_t logQueue;
96+
if(stdOut || stdErr)
97+
{
98+
logQueue = dispatch_queue_create("com.opa334.TrollStore.LogCollector", NULL);
99+
sema = dispatch_semaphore_create(0);
100+
101+
int outPipe = out[0];
102+
int outErrPipe = outErr[0];
103+
104+
__block BOOL outEnabled = (BOOL)stdOut;
105+
__block BOOL errEnabled = (BOOL)stdErr;
106+
dispatch_async(logQueue, ^
107+
{
108+
while(_isRunning)
109+
{
110+
@autoreleasepool
111+
{
112+
if(outEnabled)
113+
{
114+
[outString appendString:getNSStringFromFile(outPipe)];
115+
}
116+
if(errEnabled)
117+
{
118+
[errString appendString:getNSStringFromFile(outErrPipe)];
119+
}
120+
}
121+
}
122+
dispatch_semaphore_signal(sema);
123+
});
124+
}
125+
126+
do
127+
{
128+
if (waitpid(task_pid, &status, 0) != -1) {
129+
NSLog(@"Child status %d", WEXITSTATUS(status));
130+
} else
131+
{
132+
perror("waitpid");
133+
_isRunning = NO;
134+
return -222;
135+
}
136+
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
137+
138+
_isRunning = NO;
139+
if(stdOut || stdErr)
140+
{
141+
if(stdOut)
142+
{
143+
close(out[1]);
144+
}
145+
if(stdErr)
146+
{
147+
close(outErr[1]);
148+
}
149+
150+
// wait for logging queue to finish
151+
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
152+
153+
if(stdOut)
154+
{
155+
*stdOut = outString.copy;
156+
}
157+
if(stdErr)
158+
{
159+
*stdErr = errString.copy;
160+
}
161+
}
162+
163+
return WEXITSTATUS(status);
164+
}
165+
166+
NSString* getNSStringFromFile(int fd)
167+
{
168+
NSMutableString* ms = [NSMutableString new];
169+
ssize_t num_read;
170+
char c;
171+
if(!fd_is_valid(fd)) return @"";
172+
while((num_read = read(fd, &c, sizeof(c))))
173+
{
174+
[ms appendString:[NSString stringWithFormat:@"%c", c]];
175+
if(c == '\n') break;
176+
}
177+
return ms.copy;
178+
}
179+
180+
int fd_is_valid(int fd)
181+
{
182+
return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
183+
}
184+
185+
@end

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ include $(THEOS)/makefiles/common.mk
77

88
APPLICATION_NAME = RebootTools
99

10-
RebootTools_FILES = AppDelegate.swift RootViewController.swift
10+
RebootTools_FILES = AppDelegate.swift RootViewController.swift DeviceController.m
1111
RebootTools_FRAMEWORKS = UIKit CoreGraphics
1212
RebootTools_RESOURCES = Resources/Assets.xcassets
13-
13+
$(APPLICATION_NAME)_SWIFT_BRIDGING_HEADER += $(APPLICATION_NAME)-Bridging-Header.h
1414
include $(THEOS_MAKE_PATH)/application.mk
1515

1616
$(APPLICATION_NAME)_CODESIGN_FLAGS = -Sentitlements.plist
1717

18-
SUBPROJECTS += RebootRootHelper
18+
# SUBPROJECTS += RebootRootHelper
1919
include $(THEOS_MAKE_PATH)/aggregate.mk
2020

2121
after-package::

RebootTools-Bridging-Header.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#import "DeviceController.h"

Resources/RebootRootHelper

195 KB
Binary file not shown.

RootViewController.swift

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,22 @@ class RootViewController: UIViewController {
4242
}
4343

4444
// 重启按钮
45-
let rebootButton = UIButton()
46-
rebootButton.setTitle(NSLocalizedString("Reboot_Device_text", comment: ""), for: .normal)
45+
let rebootButton = UIButton(type: .system)
46+
if #available(iOS 15.0, *) {
47+
rebootButton.configuration = .filled()
48+
rebootButton.setTitle(NSLocalizedString("Reboot_Device_text", comment: ""), for: .normal)
49+
} else {
50+
// iOS 14 及以下版本 没这效果只能硬凑了
51+
rebootButton.backgroundColor = UIColor.systemBlue
52+
rebootButton.layer.cornerRadius = 8
53+
rebootButton.clipsToBounds = true
54+
rebootButton.setTitle(NSLocalizedString("Reboot_Device_text", comment: ""), for: .normal)
55+
rebootButton.setTitleColor(.white, for: .normal)
56+
}
57+
4758
rebootButton.translatesAutoresizingMaskIntoConstraints = false
59+
// 添加点击事件
60+
rebootButton.addTarget(self, action: #selector(onClickRebootButton), for: .touchUpInside)
4861

4962
// 添加设置项
5063
let showAlertSwitch = UISwitch()
@@ -61,6 +74,8 @@ class RootViewController: UIViewController {
6174
// 将开关和标签添加到子视图中
6275
showAlertSubView.addSubview(showAlertLabel)
6376
showAlertSubView.addSubview(showAlertSwitch)
77+
// TODO 暂时隐藏 后期直接整合到设置项里
78+
showAlertSubView.isHidden = true
6479

6580
// 向View中添加控件
6681
self.view.addSubview(iconImageView)
@@ -82,9 +97,10 @@ class RootViewController: UIViewController {
8297
checkPermissionLabel.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20), // 右侧边距
8398

8499
rebootButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), // 水平居中
100+
rebootButton.heightAnchor.constraint(equalToConstant: 50),
85101
rebootButton.topAnchor.constraint(equalTo: checkPermissionLabel.bottomAnchor, constant: 30),
86-
rebootButton.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20), // 左侧边距
87-
rebootButton.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20), // 右侧边距
102+
rebootButton.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 50), // 左侧边距
103+
rebootButton.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -50), // 右侧边距
88104

89105
showAlertSubView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
90106
showAlertSubView.topAnchor.constraint(equalTo: rebootButton.bottomAnchor, constant: 30),
@@ -107,6 +123,11 @@ class RootViewController: UIViewController {
107123
let writeable = access(path, W_OK) == 0
108124
return writeable
109125
}
126+
127+
@objc func onClickRebootButton() {
128+
let deviceController = DeviceController()
129+
deviceController.rebootDevice()
130+
}
110131
}
111132

112133
extension UIColor {

0 commit comments

Comments
 (0)