Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 276 lines (231 sloc) 6.156 kb
5d76d7aa »
2010-08-29 initial commit
1 package main
2
3 import (
01e0273e »
2010-08-30 add copy/delete logic if rename fails.
4 "bufio"
5 "container/list"
5d76d7aa »
2010-08-29 initial commit
6 "exec"
7 "flag"
8 "fmt"
01e0273e »
2010-08-30 add copy/delete logic if rename fails.
9 "io"
10 "os"
5d76d7aa »
2010-08-29 initial commit
11 "strings"
a774331d »
2010-08-30 use syscall.Rename rather than os.Rename to avoid mess with os.LinkEr…
12 "syscall"
5d76d7aa »
2010-08-29 initial commit
13 )
14
15 const (
16 PKGCACHE = "/var/cache/pacman/pkg"
17 )
18
d7578fea »
2010-09-08 refactor daemon ftw
19 type archPkg struct {
6efc149d »
2010-08-29 No changes made
20 Name string
21 Version string
22 Arch string
23 Ext string
24 }
25
26 /* flags */
5d76d7aa »
2010-08-29 initial commit
27 var (
28 help = flag.Bool("h", false, "display this help message")
29 cachePath = flag.String("c", PKGCACHE, "path to package cache")
32da2a6b »
2010-08-30 cleanup, sanity checks, start work on destPath option
30 destPath = flag.String("m", "", "move files to this location")
5d76d7aa »
2010-08-29 initial commit
31 keep = flag.Int("k", 3, "number of packages to keep")
32 delete = flag.Bool("d", false, "delete packages over keep limit")
33 quiet = flag.Bool("q", false, "don't print deletion candidates")
34 )
35
6efc149d »
2010-08-29 No changes made
36 /* global listing of package groups */
5d76d7aa »
2010-08-29 initial commit
37 var pkgList = map[string]*list.List{}
38
d7578fea »
2010-09-08 refactor daemon ftw
39 func addPkgSorted(lst *list.List, pkg *archPkg) {
40 p := lst.Front()
41 ele := (p.Value).(*archPkg)
5d76d7aa »
2010-08-29 initial commit
42
43 for {
d7578fea »
2010-09-08 refactor daemon ftw
44 if vercmp(pkg.Version, ele.Version) == -1 {
45 lst.InsertBefore(pkg, p)
5d76d7aa »
2010-08-29 initial commit
46 return
47 }
48 if next := p.Next(); next == nil {
d7578fea »
2010-09-08 refactor daemon ftw
49 lst.PushBack(pkg)
5d76d7aa »
2010-08-29 initial commit
50 return
51 } else {
52 p = p.Next()
53 }
54 }
55 }
56
d7578fea »
2010-09-08 refactor daemon ftw
57 func parsePkg(file string) *archPkg {
5d76d7aa »
2010-08-29 initial commit
58 parts := strings.Split(file, "-", -1)
59 n := len(parts)
60 if n < 3 {
61 return nil
62 }
63
64 pkgname := strings.Join(parts[0:n-3], "-")
65 if pkgname == "" {
66 return nil
67 }
68
69 pkgver := strings.Join(parts[n-3:n-1], "-")
ec1e9770 »
2010-08-29 handle different architectures separately
70 ext := strings.Split(parts[n-1], ".", 2)
5d76d7aa »
2010-08-29 initial commit
71
d7578fea »
2010-09-08 refactor daemon ftw
72 return &archPkg{pkgname, pkgver, ext[0], ext[1]}
5d76d7aa »
2010-08-29 initial commit
73 }
74
10409a52 »
2010-09-03 rename function doCopy => copyFile
75 func copyFile(src io.Reader, dst *bufio.Writer) os.Error {
01e0273e »
2010-08-30 add copy/delete logic if rename fails.
76 defer dst.Flush()
77
78 buf := make([]byte, os.Getpagesize())
79 for {
80
81 nr, err := src.Read(buf)
82 if err != nil && err != os.EOF {
83 return err
84 }
85
86 if nr > 0 {
87 _, err = dst.Write(buf[0:nr])
88 if err != nil {
89 fmt.Fprintln(os.Stderr, err.String())
90 break
91 }
92 } else {
93 return nil
94 }
95 }
96
97 return nil
98 }
99
d7578fea »
2010-09-08 refactor daemon ftw
100 func movePkg(pkg *archPkg) os.Error {
32da2a6b »
2010-08-30 cleanup, sanity checks, start work on destPath option
101 filename := fmt.Sprintf("%s-%s-%s.%s", pkg.Name, pkg.Version, pkg.Arch, pkg.Ext)
7de62999 »
2010-09-03 cut back on fmt.Sprintf calls
102 srcPath := fmt.Sprintf("%s/%s", *cachePath, filename)
01e0273e »
2010-08-30 add copy/delete logic if rename fails.
103
104 /* try to rename, first */
7de62999 »
2010-09-03 cut back on fmt.Sprintf calls
105 errno := syscall.Rename(srcPath, fmt.Sprintf("%s/%s", *destPath, filename))
a774331d »
2010-08-30 use syscall.Rename rather than os.Rename to avoid mess with os.LinkEr…
106
107 switch errno {
108 case syscall.EXDEV:
7de62999 »
2010-09-03 cut back on fmt.Sprintf calls
109 src, err := os.Open(srcPath, os.O_RDONLY, 0666)
01e0273e »
2010-08-30 add copy/delete logic if rename fails.
110 if err != nil {
111 fmt.Fprintf(os.Stderr, "%s: %s\n", os.Args[0], err.String())
112 os.Exit(1)
113 }
114 defer src.Close()
115
116 sfi, err := src.Stat()
117
574ecb57 »
2010-08-30 various bug fixes for move logic
118 dst, err := os.Open(fmt.Sprintf("%s/%s", *destPath, filename) , os.O_WRONLY|os.O_CREAT|os.O_TRUNC, sfi.Permission())
01e0273e »
2010-08-30 add copy/delete logic if rename fails.
119 if err != nil {
120 fmt.Fprintf(os.Stderr, "%s: %s\n", os.Args[0], err.String())
121 os.Exit(1)
122 }
123 defer dst.Close()
124
10409a52 »
2010-09-03 rename function doCopy => copyFile
125 err = copyFile(src, bufio.NewWriter(dst))
574ecb57 »
2010-08-30 various bug fixes for move logic
126 if err == nil {
7de62999 »
2010-09-03 cut back on fmt.Sprintf calls
127 return os.Remove(srcPath)
01e0273e »
2010-08-30 add copy/delete logic if rename fails.
128 }
45be6d78 »
2010-08-30 only do copy/rm if os.Rename fails with os.EXDEV
129 return err /* different from err outside if */
01e0273e »
2010-08-30 add copy/delete logic if rename fails.
130 }
131
a774331d »
2010-08-30 use syscall.Rename rather than os.Rename to avoid mess with os.LinkEr…
132 return os.Errno(errno)
32da2a6b »
2010-08-30 cleanup, sanity checks, start work on destPath option
133 }
134
d7578fea »
2010-09-08 refactor daemon ftw
135 func addPkgToMap(pkg *archPkg) {
ec1e9770 »
2010-08-29 handle different architectures separately
136 pkgKey := fmt.Sprintf("%s$%s", pkg.Name, pkg.Arch)
137 if _, ok := pkgList[pkgKey]; !ok {
138 pkgList[pkgKey] = list.New()
139 pkgList[pkgKey].PushFront(pkg)
5d76d7aa »
2010-08-29 initial commit
140 } else {
ec1e9770 »
2010-08-29 handle different architectures separately
141 addPkgSorted(pkgList[pkgKey], pkg)
5d76d7aa »
2010-08-29 initial commit
142 }
143 }
144
b3c2c11d »
2010-08-29 cleanup output
145 func prune() (int, int) {
146 pkgCount, grpCount := 0, 0
5d76d7aa »
2010-08-29 initial commit
147
148 for _, group := range pkgList {
149 groupSz := group.Len()
150 if groupSz <= *keep {
151 continue
152 }
153 for i := 0; i < groupSz-*keep; i++ {
d7578fea »
2010-09-08 refactor daemon ftw
154 p := (group.Front().Value).(*archPkg)
5d76d7aa »
2010-08-29 initial commit
155
ec1e9770 »
2010-08-29 handle different architectures separately
156 filename := fmt.Sprintf("%s-%s-%s.%s", p.Name, p.Version, p.Arch, p.Ext)
157 fullpath := fmt.Sprintf("%s/%s", *cachePath, filename)
158
01e0273e »
2010-08-30 add copy/delete logic if rename fails.
159 switch {
160 case *delete:
5d76d7aa »
2010-08-29 initial commit
161 if !*quiet {
b3c2c11d »
2010-08-29 cleanup output
162 fmt.Println("Deleting " + filename)
5d76d7aa »
2010-08-29 initial commit
163 }
164 err := os.Remove(fullpath)
165 if err != nil {
ec1e9770 »
2010-08-29 handle different architectures separately
166 fmt.Fprintf(os.Stderr, "error deleting %s: %s\n", fullpath, err.String())
5d76d7aa »
2010-08-29 initial commit
167 os.Exit(2)
168 }
01e0273e »
2010-08-30 add copy/delete logic if rename fails.
169 case *destPath != "":
5d76d7aa »
2010-08-29 initial commit
170 if !*quiet {
01e0273e »
2010-08-30 add copy/delete logic if rename fails.
171 fmt.Println("Moving " + filename)
172 }
45be6d78 »
2010-08-30 only do copy/rm if os.Rename fails with os.EXDEV
173 err := movePkg(p)
01e0273e »
2010-08-30 add copy/delete logic if rename fails.
174 if err != nil {
175 fmt.Fprintf(os.Stderr, "error moving %s: %s\n", fullpath, err.String())
176 os.Exit(2)
177 }
178 default:
179 if !*quiet {
180 fmt.Println("Would prune: " + filename)
5d76d7aa »
2010-08-29 initial commit
181 }
182 }
b3c2c11d »
2010-08-29 cleanup output
183 pkgCount++
5d76d7aa »
2010-08-29 initial commit
184
185 group.Remove(group.Front())
186 }
b3c2c11d »
2010-08-29 cleanup output
187 grpCount++
5d76d7aa »
2010-08-29 initial commit
188 }
189
b3c2c11d »
2010-08-29 cleanup output
190 return grpCount, pkgCount
5d76d7aa »
2010-08-29 initial commit
191 }
192
45be6d78 »
2010-08-30 only do copy/rm if os.Rename fails with os.EXDEV
193 /* TODO: Reimplement alpm_pkg_vercmp() */
5d76d7aa »
2010-08-29 initial commit
194 func vercmp(a, b string) int {
195 p, _ := exec.Run("vercmp", []string{"vercmp", a, b}, os.Environ(), "/usr/bin", 0, exec.DevNull, exec.DevNull)
196 w, _ := p.Wait(0)
197
198 return w.ExitStatus()
199 }
200
59c5fd17 »
2010-08-30 move flag parsing and sanity checks to init()
201 func init() {
5d76d7aa »
2010-08-29 initial commit
202 flag.Parse()
203 flag.Usage = func() {
204 fmt.Println("usage: gobble [flags]")
205 flag.PrintDefaults()
206 os.Exit(0)
207 }
208
209 if *help {
210 flag.Usage()
211 }
212
32da2a6b »
2010-08-30 cleanup, sanity checks, start work on destPath option
213 /* Sanity checks */
214 if *delete && *destPath != "" {
215 fmt.Fprintln(os.Stderr, "error: cannot delete and move in same operation")
216 os.Exit(5)
217 }
01e0273e »
2010-08-30 add copy/delete logic if rename fails.
218
32da2a6b »
2010-08-30 cleanup, sanity checks, start work on destPath option
219 if (*delete || *destPath != "") && os.Getuid() != 0 {
220 fmt.Fprintln(os.Stderr, "error: cannot delete/move as non-root user")
221 os.Exit(5)
222 }
01e0273e »
2010-08-30 add copy/delete logic if rename fails.
223
224 if _, err := os.Stat(*destPath); *destPath != "" && err != nil {
225 fmt.Fprintf(os.Stderr, "error: %s\n", err.String())
32da2a6b »
2010-08-30 cleanup, sanity checks, start work on destPath option
226 os.Exit(5)
5d76d7aa »
2010-08-29 initial commit
227 }
228
574ecb57 »
2010-08-30 various bug fixes for move logic
229 if *keep == 0 {
230 fmt.Fprintln(os.Stderr, "error: keep cannot equal 0")
231 os.Exit(5)
232 }
59c5fd17 »
2010-08-30 move flag parsing and sanity checks to init()
233 }
574ecb57 »
2010-08-30 various bug fixes for move logic
234
59c5fd17 »
2010-08-30 move flag parsing and sanity checks to init()
235 func main() {
5d76d7aa »
2010-08-29 initial commit
236 d, err := os.Open(*cachePath, os.O_RDONLY, 0666)
237 if err != nil {
238 fmt.Fprintln(os.Stderr, err.String())
239 os.Exit(1)
240 }
241 defer d.Close()
242
243 fi, err := d.Readdir(-1)
244 if err != nil {
245 fmt.Fprintln(os.Stderr, err.String())
246 os.Exit(1)
247 }
248
574ecb57 »
2010-08-30 various bug fixes for move logic
249 if !*delete && *destPath == "" {
250 fmt.Println("Performing dry-run...")
251 }
5d76d7aa »
2010-08-29 initial commit
252
253 found := 0
254 for _, pkg := range fi {
255 if p := parsePkg(pkg.Name); p != nil {
256 addPkgToMap(p)
257 found++
258 }
259 }
260
261 switch found {
262 case 0:
263 fmt.Fprintln(os.Stderr, "error: No candidate package groups found. Are you sure this is a valid pacman cache?")
264 os.Exit(0)
265 default:
d9825737 »
2010-09-11 make discovery output more descriptive
266 fmt.Printf("%d packages in %d groups found in %s\n", found, len(pkgList), *cachePath)
5d76d7aa »
2010-08-29 initial commit
267 }
268
b3c2c11d »
2010-08-29 cleanup output
269 g, p := prune()
32da2a6b »
2010-08-30 cleanup, sanity checks, start work on destPath option
270 if *delete || *destPath != "" {
b3c2c11d »
2010-08-29 cleanup output
271 fmt.Printf("%d files pruned from %d package groups.\n", p, g)
5d76d7aa »
2010-08-29 initial commit
272 } else {
b3c2c11d »
2010-08-29 cleanup output
273 fmt.Printf("%d files would be pruned from %d package groups.\n", p, g)
5d76d7aa »
2010-08-29 initial commit
274 }
275
276 }
Something went wrong with that request. Please try again.