source: trunk/PrefsPane/src/GBDebug.m @ 1046

Revision 1015, 10.8 KB checked in by speck, 10 months ago (diff)

Lots of changes for xcode 4.1 (added delegate declarations, fixed deprecated function calls, upgraded SDK and platforms).

Line 
1/* Copyright (C) 2008-2009 Peter Speck
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <execinfo.h>
18#include <sys/stat.h>
19#include "GBDebug.h"
20#import <ExceptionHandling/NSExceptionHandler.h>
21
22const char* glimmerAuthString = "org.glimmerblocker.panelprefs";
23NSString *PROXY_JOB_LABEL = @"org.glimmerblocker.proxy";
24NSString *UPDATER_JOB_LABEL = @"org.glimmerblocker.updater";
25NSString *LAST_UPDATE_CHECK_MARKER_PATH = @"/Library/GlimmerBlocker/LastUpdateCheck/Marker.txt";
26
27
28// http://www.cocoadev.com/index.pl?StackTraces
29
30@implementation GBDebug
31
32#pragma mark ------------------- stack frame dump
33
34+ (void)debugDumpExceptionStackframeUsingBacktrace:(NSException*)ex
35{
36    NSLog(@"An exception of type %@ occured.\n%@\n", [ex name], [ex reason]);
37    NSArray* a = [ex callStackReturnAddresses];
38    if (!a) {
39        NSLog(@"Stacktrace is not available from the NSException.");
40        return;
41    }
42    int numFrames = (int)[a count];
43    void *frames[numFrames];
44    int i = 0;
45    for (NSNumber *frame in a)
46        frames[i++] = (void *)[frame unsignedIntegerValue];
47    char **texts = backtrace_symbols(frames, numFrames);
48    if (texts) {
49        NSLog(@"Stack trace:");
50        for (i = 0; i < numFrames; i++)
51            NSLog(@"%s", texts[i]);
52        free(texts);
53    } else {
54        NSLog(@"Stack trace not available from backtrace_symbols.");
55    }
56}
57
58
59+ (void)debugDumpExceptionStackframe:(NSException*)ex
60{
61    // atos is only available when devtools is installed.
62    if (![[NSFileManager defaultManager] fileExistsAtPath:@"usr/bin/atos"]) {
63        [GBDebug debugDumpExceptionStackframeUsingBacktrace:ex];
64        return;
65    }
66    NSString *stackTrace = [[ex userInfo] objectForKey:NSStackTraceKey];
67    if (!stackTrace) {
68        NSLog(@"Didn't get a stackTrace. Must enable stacktraces by calling "
69              "[[NSExceptionHandler defaultExceptionHandler] setExceptionHandlingMask:NSLogAndHandleEveryExceptionMask];");
70        return;
71    }
72    NSString *str = [NSString stringWithFormat:@"/usr/bin/atos -p %d %@ | tail -n +3 | head -n +%d | c++filt | cat -n",
73                     [[NSProcessInfo processInfo] processIdentifier],
74                     stackTrace,
75                     ([[stackTrace componentsSeparatedByString:@"  "] count] - 4)];
76    FILE *file = popen([str UTF8String], "r");
77    if (file) {
78        char buffer[512];
79        size_t length;
80        NSLog(@"An exception of type %@ occured.\n%@\n", [ex name], [ex reason]);
81        fprintf(stderr, "Stack trace:\n");
82        while ((length = fread(buffer, 1, sizeof(buffer), file)))
83            fwrite( buffer, 1, length, stderr );
84        pclose( file );
85    }
86}
87
88+ (void)debugDumpStackframeUsingBacktrace
89{
90    void* callstack[128];
91    int numFrames = backtrace(callstack, 128);
92    if (numFrames <= 0) {
93        NSLog(@"Did not get any stacktrace from backtrace()");
94    }
95    char **texts = backtrace_symbols(callstack, numFrames);
96    if (texts) {
97        NSLog(@"Stack trace:");
98        for (int i = 0; i < numFrames; i++)
99            NSLog(@"%s", texts[i]);
100        free(texts);
101    } else {
102        NSLog(@"Stack trace not available from backtrace_symbols.");
103    }
104}
105
106
107+ (void)debugDumpStackframe
108{
109    // atos is only available when devtools is installed.
110    if (![[NSFileManager defaultManager] fileExistsAtPath:@"usr/bin/atos"]) {
111        [GBDebug debugDumpStackframeUsingBacktrace];
112        return;
113    }
114    @try {
115        [NSException raise:@"dummy" format:@"my format"];
116    }
117    @catch (NSException* ex) {
118        [GBDebug debugDumpExceptionStackframe:ex];
119    }
120}
121
122#pragma mark ------------------- codesign
123
124+ (BOOL)verifyCodesignForBundle:(NSString*)identifier
125                         atPath:(NSString*)path
126{
127    NSMutableArray* args = [[NSMutableArray arrayWithCapacity:3] retain];
128    [args addObject:@"--verify"];
129    [args addObject:[NSString stringWithFormat:@"-R=identifier %@ and certificate leaf = %@",
130                     identifier, @"H\"c9d85c0c3b7a8d1cb8060aa34df69713cfff637c\""]];
131    [args addObject:path];
132    NSTask* task = [[NSTask alloc] init];
133    [task setLaunchPath:@"/usr/bin/codesign"];
134    [task setArguments:args];
135    [task setCurrentDirectoryPath:@"/tmp"];
136    NSPipe *stderrPipe = [[NSPipe pipe] retain];
137    NSFileHandle *stderrHandle = [[stderrPipe fileHandleForReading] retain];
138    [task setStandardError:stderrPipe];
139    [task launch];
140    while (1) {
141        NSData* data = [stderrHandle availableData];
142        if (!data || ![data length])
143            break;
144        NSString* s = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
145        NSLog(@"codesign: %@", s);
146        [s release];
147    }
148    [task waitUntilExit];
149    int status = [task terminationStatus];
150    if (status) {
151        NSLog(@"codesign verification FAILED (%d): %@", status, [args objectAtIndex:2]);
152        NSLog(@"  for '%@' at path: %@", identifier, path);
153    } else {
154#if 0
155        NSLog(@"codesign SUCCESS: %@", [args objectAtIndex:2]);
156#endif
157    }
158    [task release];
159    [stderrHandle release];
160    [stderrPipe release];
161    [args release];
162    return !status;
163}
164
165
166+ (BOOL)verifyCodesign
167{
168    // be paranoid about "man" leaving files all over the place.
169    unlink("/Library/PreferencePanes/GlimmerBlocker.prefPane/Contents/Resources/.lesshst");
170    return [GBDebug verifyCodesignForBundle:@"org.andymatuschak.Sparkle"
171                                     atPath: @"/Library/PreferencePanes/GlimmerBlocker.prefPane/Contents/GlimmerBlockerUpdater.app/Contents/Frameworks/Sparkle.framework/Versions/A"]
172    && [GBDebug verifyCodesignForBundle:@"org.glimmerblocker.upgradeApp"
173                                 atPath:@"/Library/PreferencePanes/GlimmerBlocker.prefPane/Contents/GlimmerBlockerUpdater.app"]
174    && [GBDebug verifyCodesignForBundle:@"org.glimmerblocker.proxyApp"
175                                 atPath:@"/Library/PreferencePanes/GlimmerBlocker.prefPane/Contents/GlimmerBlockerProxy.app"]
176    && [GBDebug verifyCodesignForBundle:@"org.glimmerblocker.prefsPane"
177                                 atPath:@"/Library/PreferencePanes/GlimmerBlocker.prefPane"];
178}
179
180#pragma mark ------------------- launchd
181
182+ (NSString*)proxyPlistPath
183{
184    return [NSString stringWithFormat:@"/Library/LaunchDaemons/%@.plist", PROXY_JOB_LABEL];
185}
186
187+ (NSString*)updaterPlistPath
188{
189    return [NSString stringWithFormat:@"/Library/LaunchAgents/%@.plist", UPDATER_JOB_LABEL];
190}
191
192+ (NSString*)updaterBundlePath
193{
194    return @"/Library/PreferencePanes/GlimmerBlocker.prefPane/Contents/GlimmerBlockerUpdater.app";
195}
196
197#pragma mark ------------------- URLs
198
199+ (NSString*)percentEscapeUrlParameterValue:(NSString*)s
200{
201    NSString* e = [s stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
202    // '&' isn't encoded by stringByAddingPercentEscapesUsingEncoding
203    e = [e stringByReplacingOccurrencesOfString:@"&" withString:@"%26"];
204    e = [e stringByReplacingOccurrencesOfString:@"+" withString:@"%2b"];
205    return e;
206}
207
208#pragma mark ------------------- GUI
209
210+ (void)setPop:(NSPopUpButton*)popup
211        values:(NSString*[])values
212 selectedValue:(NSString*)selectedValue
213  defaultValue:(NSString*)defaultValue
214{
215    NSString* sv = selectedValue ? selectedValue : defaultValue;
216    for (int i = 0; values[i]; i++) {
217        if ([values[i] isEqual:sv]) {
218            [popup selectItemAtIndex:i];
219            return;
220        }
221    }
222    NSLog(@"Did not find popup item, selectedValue = '%@', defaultValue = '%@'", selectedValue, defaultValue);
223    for (int i = 0; values[i]; i++)
224        NSLog(@"  values[%d] = '%@'", i, values[i]);
225}
226
227+ (void)setVertMatrix:(NSMatrix*)matrix
228               values:(NSString*[])values
229        selectedValue:(NSString*)selectedValue
230         defaultValue:(NSString*)defaultValue
231{
232    NSString* sv = selectedValue ? selectedValue : defaultValue;
233    for (int i = 0; values[i]; i++) {
234        if ([values[i] isEqual:sv]) {
235            [matrix deselectAllCells];
236            NSInteger rows = [matrix numberOfRows];
237            [matrix selectCellAtRow:(i % rows) column:(i / rows)];
238            return;
239        }
240    }
241    NSLog(@"Did not find popup item, selectedValue = '%@', defaultValue = '%@'", selectedValue, defaultValue);
242    for (int i = 0; values[i]; i++)
243        NSLog(@"  values[%d] = '%@'", i, values[i]);
244}
245
246#pragma mark ------------------- cmd line
247
248+ (int)executeCommandlineTool:(NSString*)cmd
249                     withArgs:(NSArray*)args
250{
251    NSLog(@"executeCommandlineTool: %@   %@", cmd, args);
252    [args retain];
253    NSTask* task = [[NSTask alloc] init];
254    [task setLaunchPath:cmd];
255    if (args)
256        [task setArguments:args];
257    [task setCurrentDirectoryPath:@"/tmp"];
258    int status = -1;
259    @try {
260        [task launch];
261        [task waitUntilExit];
262        status = [task terminationStatus];
263    }
264    @catch (NSException* ex) {
265        status = -1;
266        NSLog(@"Execution failed of %@  %@\n   with exception: %@", cmd, args, ex);
267    }
268    [task release];
269    [args release];
270    return status;
271}
272
273#pragma mark ------------------- saving
274
275+ (void)safeSaveFileAtPath:(NSString *)path contents:(NSData *)data;
276{
277    NSString* tmp = [path stringByAppendingString:@".tmp"];
278    NSFileManager* fm = [NSFileManager defaultManager];
279    NSDictionary* attrs = [NSDictionary dictionaryWithObjectsAndKeys:
280                           [NSNumber numberWithLong:0644], NSFilePosixPermissions,
281                           NULL];
282    [fm createFileAtPath:tmp contents:[NSData data] attributes:attrs];
283    NSFileHandle* fh = [NSFileHandle fileHandleForWritingAtPath:tmp]; // can only write to existing files!
284    [fh writeData:data];
285    [fh synchronizeFile];
286    [fh closeFile];
287    for (int i = 4; i >= 0; i--) {
288        NSString* p1 = i ? [NSString stringWithFormat:@"%@.old-%d", path, i] : path;
289        NSString* p2 = [NSString stringWithFormat:@"%@.old-%d", path, i + 1];
290        NSError* error = NULL;
291        if ([fm fileExistsAtPath:p2] && ![fm removeItemAtPath:p2 error:&error])
292            NSLog(@" FAILED ZAPPING %@", p2);
293        if ([fm fileExistsAtPath:p1] && ![fm moveItemAtPath:p1 toPath:p2 error:NULL]) {
294            NSLog(@"FAILED RENAMING %@", p1);
295            NSLog(@"             TO %@", p2);
296        }
297    }
298    if (![fm moveItemAtPath:tmp toPath:path error:NULL]) {
299        NSLog(@"FAILED RENAMING %@", tmp);
300        NSLog(@"             TO %@", path);
301    }
302}
303
304
305#pragma mark ------------------- inode fingerprint
306
307+ (NSString*)fingerprintOptionForInstalledPanel:(NSBundle*)prefsPanelBundle
308{
309    NSString* vers = [[prefsPanelBundle infoDictionary] objectForKey:@"CFBundleVersion"];
310    return [NSString stringWithFormat:@"<string>--gb-version=%@</string>",
311            vers ? vers : @"unknown"];
312}
313
314+ (BOOL)fingerprintForRunningServerIsValid:(NSBundle*)prefsPanelBundle
315{
316    NSString* jobLabel = @"org.glimmerblocker.proxy";
317    NSString* plistPath = [NSString stringWithFormat:@"/Library/LaunchDaemons/%@.plist", jobLabel];
318    NSFileManager* fm = [NSFileManager defaultManager];
319    if (![fm fileExistsAtPath:plistPath])
320        return YES;
321    NSData* data = [fm contentsAtPath:plistPath];
322    if (!data)
323        return NO;
324    NSString* expected = [GBDebug fingerprintOptionForInstalledPanel:prefsPanelBundle];
325    NSString* s = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
326    BOOL ok = [s rangeOfString:expected].location != NSNotFound;
327    if (!ok)
328        DebugNSLog(@"Did not have fingerprint {{%@}} in %@", expected, s);
329    [s release];
330    NSLog(@"Fingerprint for running server is %@.", ok ? @"ok" : @"missing/bad");
331    return ok;
332}
333
334@end
Note: See TracBrowser for help on using the repository browser.