포트를 만들거나 업글할 때 가장 귀찮은 과정 중의 하나인 plist를 자동으로 만드는 방법은 mtree기반으로 된 것이 있었지만, 뭔가 다른 프리픽스로 한 번 깔아보는 게 대략 귀찮은 관계로.. ktrace 를 이용한 방법을 한 번 생각해 봤습니다. 흐흐흐.. open 시스템 콜을 모두 추적해서 O_WRONLY나 O_CREAT로 열린 녀석들 목록을 자동으로 plist로 만들어주는 것인데, 대충 만들어 본 소스는..
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
<pre> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
#!/usr/bin/env python # # autoplist.py - Automatic pkg-plist generator based on ktrace # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42, (c) Poul-Henning Kamp): # Hye-Shik Chang <perky@FreeBSD.org> wrote this file. As long as you retain # this notice you can do whatever you want with this stuff. If we meet some # day, and you think this stuff is worth it, you can buy me a beer in return. # # Hye-Shik Chang # ---------------------------------------------------------------------------- # $FreeBSD$ import os, random import sys, re import time random.whseed() TMPNAM = '.autoplist.%d.%x' % (os.getpid(), random.randrange(65536)) KTRACECMD = 'ktrace -f %s -id -t cn %%s' % TMPNAM KDUMPCMD = 'kdump -f %s' % TMPNAM O_WFLAGS = os.O_WRONLY | os.O_RDWR | os.O_CREAT PREFIX = '/usr/local' IGNOREPREFIX = ['/dev/', '/tmp/'] PLIST_SUBS = ( 'PYTHON_INCLUDEDIR', 'PYTHON_LIBDIR', 'PYTHON_SITELIBDIR', 'SITE_PERL', 'SITE_RUBY', 'DOCSDIR', 'EXAMPLESDIR', 'DATADIR', ) PLISTREPLACE = [] def errorexit(msg): print >> sys.stdout, "autoplist.py:", msg sys.exit(-1) def cleanup_tmpfiles(): try: os.unlink(TMPNAM) except: pass def getmakeenv(envname): return os.popen('make -V "%s"' % envname).read().strip() def echo_msg(msg): print '===> ', msg class InstallState: procs = {} lastforker = 0 def __init__(self, pid, procname): self.pid = pid self.procname = procname if not self.lastforker: self.curdir = os.getcwd() else: self.curdir = self.procs[self.lastforker].curdir self.procs[pid] = self self.files, self.dirs = {}, {} self.trackatom = {} def feed(self, args): if self.trackatom: if args[2] == 'NAMI': self.trackatom['nami'].append(eval(args[3])) elif args[2] == 'RET': rtok = args[3].split() if rtok[1].isdigit() or rtok[1].startswith('0x'): self.trackatom['exit'] = eval(rtok[1]) else: self.trackatom['exit'] = rtok[1] if len(rtok) >= 3: self.trackatom['errno'] = eval(rtok[3]) self.newsyscall(**self.trackatom) self.trackatom = {} elif args[2] == 'CALL': syscall, ignore, args = re_syscallargs.findall(args[3])[0] self.trackatom['syscall'] = syscall self.trackatom['args'] = args and args.split(',') or [] self.trackatom['nami'] = [] if syscall in ('fork', 'vfork', 'rfork'): InstallState.lastforker = self.pid def getabspath(self, path): if path.startswith('/'): return path else: return os.path.realpath(os.sep.join([self.curdir, path])) def newsyscall(self, syscall, args, nami, exit, errno=0): if syscall == 'open': if eval(args[1]) & O_WFLAGS: self.files[self.getabspath(nami[0])] = None elif syscall == 'mkdir': self.dirs[self.getabspath(nami[0])] = None elif syscall == 'chdir' and exit == 0: self.curdir = self.getabspath(nami[0]) elif syscall in ('execve',) and exit == 0: self.procname = nami[0] def filterfilelist(files): filelist = [] warnlist = [] prefix = PREFIX + os.sep for f in files: for pfx in IGNOREPREFIX: if f.startswith(pfx): continue if f.startswith(prefix): filelist.append(f[len(prefix):]) for var, repl in PLISTREPLACE: if filelist[-1].startswith(repl): filelist[-1] = filelist[-1].replace(repl, '%%'+var+'%%') else: warnlist.append(f) return filelist, warnlist re_syscallargs = re.compile('([A-Za-z0-9_-]+)(\(([^)]*)\))?') def getwfiles(): entry = {} istate = InstallState.procs echo_msg('Analyzing installation syscall logs') for l in os.popen(KDUMPCMD): ltok = l[:-1].split(None, 3) pid = int(ltok[0]) if not istate.has_key(pid): proc = istate[pid] = InstallState(pid, ltok[1]) else: proc = istate[pid] proc.feed(ltok) echo_msg('Generating auto plist') fo = open("pkg-plist.autogen", "w") print >>fo, "@comment Generated by autoplist.py", time.asctime() files = {} dirs = {} for pid, data in istate.iteritems(): files.update(data.files) dirs.update(data.dirs) files = files.keys() files.sort() files, warnfiles = filterfilelist(files) for file in files: print >>fo, file dirs = dirs.keys() dirs.sort() dirs.reverse() dirs, warndirs = filterfilelist(dirs) for dir in dirs: print >>fo, "@dirrm", dir return istate def main(): try: err = os.system('make build') if err != 0: errorexit("`make' exited with error code %d" % err) err = os.system(KTRACECMD % 'make install') if err != 0: errorexit("`make' exited with error code %d" % err) getwfiles() finally: cleanup_tmpfiles() def buildenvironment(): global IGNOREPREFIX, PREFIX, PLISTREPLACE IGNOREPREFIX.append(os.path.abspath(getmakeenv('WRKDIRPREFIX'))) IGNOREPREFIX.append(os.path.abspath(getmakeenv('DISTDIR'))) IGNOREPREFIX.append(os.path.abspath(getmakeenv('PKG_DBDIR'))) PREFIX = getmakeenv('PREFIX') for var, raw, stripped in re.findall('([A-Za-z_0-9]+)=("([^"]*)"|[^"]\S*)', getmakeenv('PLIST_SUB')): if var in PLIST_SUBS: PLISTREPLACE.append((var, stripped or raw)) PLISTREPLACE.sort(lambda x,y: -cmp(x[1], y[1])) if __name__ == '__main__': buildenvironment() main() # ex: ts=9 sts=4 sw=4 et |
흐흐.. 그런데, 대충 만들고 나서 보니, 속도가 너무 느리고 (좀만 대형 포트로 가면 엄청난 양의 kdump가 나와버려서 그것 파싱하는데 십분이 넘게 걸립니다 –;) mtree기반의 녀석에게 특별한 장점이 없다는 결론이.. 흐흐흐… (그리고, 위 구현에서는 디렉토리를 인스톨 도중에 mv 해 버린다던지 하는 것에 대해서 무방비 –;)
그래서, 이 녀석을 좀 변모시켜서 plistlint 정도의 이름으로 인스톨하면서 plist에 없는 프로그램 건드리지는 않나, 빼먹은 파일 없나, /tmp에 이상한 파일 남기지 않나 등등을 검사하는 걸로 만들어 볼 까 합니당. 크크
천재 퍽희언니
잘쓸께여~ 퍼키를 퍽희라니.. fuck 을 희 한다 정도로 읽혀지는.. 안습ㅠ
저도 사실 댓글 보고 K-Dog님이랑 같은 생각을 했… =3==3
ㅎㅎ 저도 그렇게 생각을.. =3=3
쳇 안해요!
오… 멋져요… 🙂
Windows Mobile Device를 컴퓨터 여러 대와 동기화하기…에 적용할 방법은 없을까요.. ;ㅁ;
언니..ㅋ 잘어울려요..ㅋ
와 이런 것도 가능하군요. 대단해요
소스를 보니 맥 오에스만 되는거 같네요 ^^;
윈도우에서도 파일 경로와 관련된 것만 고쳐주시면 잘될 것 같습니다~
이거 되는 유틸을 하나 만들어 보려고 했는데 먼저 하신분이 계시네요… ^_^