4 package require inifile
5 package require fileutil
7 set reposdir /etc/zypp/repos.d
12 if [regexp -- {^file(\d+)$} $file match fd] {
18 set ::globalarray_cnt 0
21 set name "::globalarray_[incr ::globalarray_cnt]"
22 array set $name [list varName $name]
26 ### generic repo handling (cache reading/writing)
28 proc repo_calc_cookie_file {selfName filename} {
30 set chksum [solv::new_Chksum $solv::REPOKEY_TYPE_SHA256]
32 $chksum add_stat $filename
36 proc repo_calc_cookie_fp {selfName fp} {
38 set chksum [solv::new_Chksum $solv::REPOKEY_TYPE_SHA256]
44 proc repo_calc_cookie_ext {selfName f cookie} {
46 set chksum [solv::new_Chksum $solv::REPOKEY_TYPE_SHA256]
49 $chksum add_fstat [$f fileno]
53 proc repo_cachepath {selfName {ext "-"}} {
55 regsub {^\.} $self(name) _ path
57 set path "${path}_$ext.solvx"
59 set path "${path}.solv"
61 regsub -all / $path _ path
62 return "/var/cache/solv/$path"
65 proc repo_generic_load {selfName pool} {
67 set handle [ $pool add_repo $self(name) ]
68 set self(handle) $handle
69 $handle configure -priority [expr 99 - $self(priority)] -appdata $self(varName)
70 set dorefresh $self(autorefresh)
71 set metadata_expire $self(metadata_expire)
73 if {$metadata_expire == -1 || [clock seconds] - [file mtime [repo_cachepath self]] < $metadata_expire} {
78 set self(extcookie) {}
79 if { !$dorefresh && [repo_usecachedrepo self] } {
80 puts "repo $self(name): cached"
86 proc repo_free_handle {selfName} {
88 set handle $self(handle)
93 proc repo_usecachedrepo {selfName {ext "-"} {mark 0}} {
95 set repopath [repo_cachepath self $ext]
97 set f [open $repopath "rb"]
99 set fcookie [read $f 32]
100 set cookie [expr {$ext eq "-" ? $self(cookie) : $self(extcookie)}]
101 if {$cookie ne {} && $cookie ne $fcookie} {
106 if {$ext eq "-" && $self(type) ne "system"} {
108 set fextcookie [read $f 32]
111 set ff [solv::xfopen_fd {} [fileno $f]]
115 set flags [expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES]
117 set flags [expr $flags | $solv::Repo_REPO_LOCALPOOL]
120 if {! [$self(handle) add_solv $ff $flags]} {
125 if {$self(type) ne "system" && $ext eq "-"} {
126 set self(cookie) $fcookie
127 set self(extcookie) $fextcookie
131 ::fileutil::touch -c -m -t [clock seconds] $repopath
136 return [expr {$code == 2 ? $res : 0}]
139 proc repo_writecachedrepo {selfName {ext "-"} {repodata "NULL"}} {
141 if [info exists self(incomplete)] {
144 file mkdir "/var/cache/solv"
145 ::fileutil::tempdir "/var/cache/solv"
146 set tempfilename [::fileutil::tempfile ".newsolv-"]
147 ::fileutil::tempdirReset
148 set f [solv::xfopen $tempfilename "w+"]
149 file attributes $tempfilename -permissions 0444
150 if {$repodata eq {NULL}} {
151 $self(handle) write $f
156 if {$self(type) ne "system" && $ext eq "-"} {
157 if {$self(extcookie) eq {}} {
158 set self(extcookie) [repo_calc_cookie_ext self $f $self(cookie)]
160 $f write $self(extcookie)
162 $f write [expr {$ext eq "-" ? $self(cookie) : $self(extcookie)}]
164 file rename -force -- $tempfilename [repo_cachepath self $ext]
167 proc repo_download {selfName file uncompress chksum {markincomplete 0}} {
169 regsub {/$} $self(baseurl) {} url
171 set tempfilename [::fileutil::tempfile]
172 set f [open $tempfilename rb+]
173 file delete -- $tempfilename
175 exec -ignorestderr -- curl -f -s -L $url ">@$f"
178 if {($chksum ne "" && $chksum ne "NULL") || [tell $f] != 0} {
179 puts "$file: download error"
185 if {$chksum ne "" && $chksum ne "NULL"} {
186 set fchksum [solv::new_Chksum [$chksum cget -type]]
187 if {$fchksum eq "" || $fchksum eq "NULL"} {
188 puts "$file: unknown checksum type"
189 if {$markincomplete} {
190 set self(incomplete) 1
195 $fchksum add_fd [fileno $f]
196 if {[$fchksum != $chksum]} {
197 puts "$file: checksum mismatch"
198 if {$markincomplete} {
199 set self(incomplete) 1
205 set ff [solv::xfopen_fd [expr {$uncompress ? $file : ""}] [fileno $f]]
210 proc repo_generic_add_ext_keys {selfName ext repodata h} {
213 $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOSITORY_DELTAINFO
214 $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOKEY_TYPE_FLEXARRAY
215 } elseif {$ext eq "DU"} {
216 $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::SOLVABLE_DISKUSAGE
217 $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOKEY_TYPE_DIRNUMNUMARRAY
218 } elseif {$ext eq "FL"} {
219 $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::SOLVABLE_FILELIST
220 $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOKEY_TYPE_DIRSTRARRAY
226 proc repo_system_load {selfName pool} {
228 set handle [ $pool add_repo $self(name) ]
229 set self(handle) $handle
230 $handle configure -appdata $self(varName)
231 $pool configure -installed $handle
232 puts -nonewline "rpm database: "
233 set self(cookie) [repo_calc_cookie_file self "/var/lib/rpm/Packages"]
234 if [repo_usecachedrepo self] {
239 set f [solv::xfopen [repo_cachepath self]]
240 $handle add_rpmdb_reffp $f $solv::Repo_REPO_REUSE_REPODATA
241 repo_writecachedrepo self
246 proc repo_repomd_add_ext {selfName repodata what ext} {
248 set where [repo_repomd_find self $what]
252 set h [$repodata new_handle]
253 $repodata set_poolstr $h $solv::REPOSITORY_REPOMD_TYPE $what
254 $repodata set_str $h $solv::REPOSITORY_REPOMD_LOCATION [lindex $where 0]
255 $repodata set_checksum $h $solv::REPOSITORY_REPOMD_CHECKSUM [lindex $where 1]
256 repo_generic_add_ext_keys self $ext $repodata $h
257 $repodata add_flexarray $solv::SOLVID_META $solv::REPOSITORY_EXTERNAL $h
260 proc repo_repomd_add_exts {selfName} {
262 set repodata [$self(handle) add_repodata 0]
263 $repodata extend_to_repo
264 repo_repomd_add_ext self $repodata "filelists" "FL"
265 $repodata internalize
268 proc repo_repomd_find {selfName what} {
270 set di [$self(handle) Dataiterator_meta $solv::REPOSITORY_REPOMD_TYPE $what $solv::Dataiterator_SEARCH_STRING]
271 $di prepend_keyname $solv::REPOSITORY_REPOMD
273 set dp [$d parentpos]
274 set filename [$dp lookup_str $solv::REPOSITORY_REPOMD_LOCATION]
275 set checksum [$dp lookup_checksum $solv::REPOSITORY_REPOMD_CHECKSUM]
276 if {$filename ne "" && $checksum eq "NULL"} {
277 puts "no $filename file checksum"
278 } elseif {$filename ne ""} {
279 return [list $filename $checksum]
285 proc repo_repomd_load {selfName pool} {
287 if [repo_generic_load self $pool] {
290 puts -nonewline "rpmmd repo '$self(name)': "
291 set f [repo_download self {repodata/repomd.xml} 0 {}]
293 puts "no repomd.xml file, skipped"
294 repo_free_handle self
297 set self(cookie) [repo_calc_cookie_fp self $f]
298 if [repo_usecachedrepo self "-" 1] {
302 set handle $self(handle)
303 $handle add_repomdxml $f
305 set primary [repo_repomd_find self primary]
306 if {$primary ne {}} {
307 set f [repo_download self [lindex $primary 0] 1 [lindex $primary 1] 1]
309 $handle add_rpmmd $f {}
312 if [info exists self(incomplete)] {
316 set updateinfo [repo_repomd_find self primary]
317 if {$updateinfo ne {}} {
318 set f [repo_download self [lindex $updateinfo 0] 1 [lindex $updateinfo 1] 1]
320 $handle add_updateinfoxml $f
324 repo_repomd_add_exts self
325 repo_writecachedrepo self
326 $self(handle) create_stubs
330 proc repo_repomd_packagespath {selfName} {
334 proc repo_repomd_load_ext {selfName repodata} {
336 switch -- [$repodata lookup_str $solv::SOLVID_META $solv::REPOSITORY_REPOMD_TYPE] {
347 puts -nonewline "\[$self(name):$ext: "
349 if [repo_usecachedrepo self $ext] {
354 set handle $self(handle)
355 set filename [$repodata lookup_str $solv::SOLVID_META $solv::REPOSITORY_REPOMD_LOCATION]
356 set filechecksum [$repodata lookup_checksum $solv::SOLVID_META $solv::REPOSITORY_REPOMD_CHECKSUM]
357 set f [repo_download self $filename 1 $filechecksum]
362 $handle add_rpmmd $f "FL" [ expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES | $solv::Repo_REPO_LOCALPOOL]
365 repo_writecachedrepo self $ext $repodata
371 proc repo_susetags_add_ext {selfName repodata what ext} {
373 set where [repo_susetags_find self $what]
377 set h [$repodata new_handle]
378 $repodata set_str $h $solv::SUSETAGS_FILE_NAME [lindex $where 0]
379 $repodata set_checksum $h $solv::SUSETAGS_FILE_CHECKSUM [lindex $where 1]
380 repo_generic_add_ext_keys self $ext $repodata $h
381 $repodata add_flexarray $solv::SOLVID_META $solv::REPOSITORY_EXTERNAL $h
384 proc repo_susetags_add_exts {selfName} {
386 set repodata [$self(handle) add_repodata 0]
387 $repodata extend_to_repo
388 repo_susetags_add_ext self $repodata "packages.FL" "FL"
389 repo_susetags_add_ext self $repodata "packages.FL.gz" "FL"
390 $repodata internalize
393 proc repo_susetags_find {selfName what} {
395 set di [$self(handle) Dataiterator_meta $solv::SUSETAGS_FILE_NAME $what $solv::Dataiterator_SEARCH_STRING]
396 $di prepend_keyname $solv::SUSETAGS_FILE
398 set dp [$d parentpos]
399 set checksum [$dp lookup_checksum $solv::SUSETAGS_FILE_CHECKSUM]
400 return [list $what $checksum]
405 proc repo_susetags_load {selfName pool} {
407 if [repo_generic_load self $pool] {
410 puts -nonewline "susetags repo '$self(name)': "
411 set f [repo_download self {content} 0 {}]
413 puts "no content file, skipped"
414 repo_free_handle self
417 set self(cookie) [repo_calc_cookie_fp self $f]
418 if [repo_usecachedrepo self "-" 1] {
422 set handle $self(handle)
423 $handle add_content $f
425 set defvendorid [[$handle cget -meta] lookup_id $solv::SUSETAGS_DEFAULTVENDOR]
426 set descrdir [[$handle cget -meta] lookup_str $solv::SUSETAGS_DESCRDIR]
427 if {$descrdir eq {NULL}} {
428 set descrdir "suse/setup/descr"
430 set packages [repo_susetags_find self "packages.gz"]
431 if {$packages eq {}} {
432 set packages [repo_susetags_find self "packages"]
434 if {$packages ne {}} {
435 set f [repo_download self "$descrdir/[lindex $packages 0]" 1 [lindex $packages 1] 1]
437 $handle add_susetags $f $defvendorid {} [expr $solv::Repo_REPO_NO_INTERNALIZE | $solv::Repo_SUSETAGS_RECORD_SHARES]
439 set packages [repo_susetags_find self "packages.en.gz"]
440 if {$packages eq {}} {
441 set packages [repo_susetags_find self "packages.en"]
443 if {$packages ne {}} {
444 set f [repo_download self "$descrdir/[lindex $packages 0]" 1 [lindex $packages 1] 1]
446 $handle add_susetags $f $defvendorid {} [expr $solv::Repo_REPO_NO_INTERNALIZE | $solv::Repo_REPO_REUSE_REPODATA | $solv::Repo_REPO_EXTEND_SOLVABLES ]
453 repo_susetags_add_exts self
454 repo_writecachedrepo self
455 $self(handle) create_stubs
459 proc repo_susetags_packagespath {selfName} {
461 set datadir [[$self(handle) cget -meta] lookup_str $solv::SUSETAGS_DATADIR]
462 return [expr {$datadir ne {} ? "$datadir/" : "suse/"}]
465 proc repo_susetags_load_ext {selfName repodata} {
467 set filename [$repodata lookup_str $solv::SOLVID_META $solv::SUSETAGS_FILE_NAME]
468 set ext [string range $filename 9 10]
469 puts -nonewline "\[$self(name):$ext: "
471 if [repo_usecachedrepo self $ext] {
476 set handle $self(handle)
477 set defvendorid [[$handle cget -meta] lookup_id $solv::SUSETAGS_DEFAULTVENDOR]
478 set descrdir [[$handle cget -meta] lookup_str $solv::SUSETAGS_DESCRDIR]
479 if {$descrdir eq {NULL}} {
480 set descrdir "suse/setup/descr"
482 set filechecksum [$repodata lookup_checksum $solv::SOLVID_META $solv::SUSETAGS_FILE_CHECKSUM]
483 set f [repo_download self "$descrdir/$filename" 1 $filechecksum]
487 set flags [expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES]
489 set flags [expr $flags | $solv::Repo_REPO_LOCALPOOL]
491 $handle add_susetags $f $defvendorid $ext $flags
493 repo_writecachedrepo self $ext $repodata
499 proc repo_unknown_load {selfName pool} {
501 puts "unsupported repo '$self(name)': skipped"
507 proc repo_load {selfName pool} {
509 "repo_$self(type)_load" self $pool
512 proc repo_packagespath {selfName} {
514 "repo_$self(type)_packagespath" self
517 proc repo_load_ext {selfName repodata} {
519 "repo_$self(type)_load_ext" self $repodata
524 proc load_stub {repodata} {
526 upvar #0 [[$repodata cget -repo] cget -appdata] repo
527 if [info exists repo(handle)] {
528 return [repo_load_ext repo $repodata]
542 foreach reponame [lsort [glob -nocomplain -directory $reposdir *.repo]] {
543 set ini [::ini::open $reponame r]
544 foreach alias [::ini::sections $ini] {
545 upvar #0 [globalarray] repo
546 array set repo {enabled 0 priority 99 autorefresh 1 type rpm-md metadata_expire 900}
547 array set repo [::ini::get $ini $alias]
548 set repo(name) $alias
549 switch -exact -- $repo(type) {
550 rpm-md { set repo(type) repomd }
551 yast2 { set repo(type) susetags }
552 default { set repo(type) unknown }
554 lappend repoNames $repo(varName)
559 set pool [solv::new_Pool]
561 $pool set_loadcallback load_stub
563 upvar #0 [globalarray] sysrepo
564 array set sysrepo [list name {@System} type system]
565 repo_load sysrepo $pool
567 foreach repoName $repoNames {
568 upvar 0 $repoName repo
569 if {$repo(enabled)} {
575 set cmd [lindex $::argv 0]
576 set ::argv [lreplace $::argv 0 0]
578 array set cmdabbrev [ list \
585 if [info exists cmdabbrev($cmd)] {
586 set cmd $cmdabbrev($cmd)
589 if {$cmd eq "search"} {
590 set arg [lindex $::argv 0]
591 $pool createwhatprovides
592 set sel [$pool Selection]
593 set di [$pool Dataiterator $solv::SOLVABLE_NAME $arg [ expr $solv::Dataiterator_SEARCH_SUBSTRING | $solv::Dataiterator_SEARCH_NOCASE ]]
595 $sel add_raw $solv::Job_SOLVER_SOLVABLE [$d cget -solvid]
597 foreach s [$sel solvables] {
598 puts [format { - %s [%s]: %s} [$s str] [[$s cget -repo] cget -name] [$s lookup_str $solv::SOLVABLE_SUMMARY]]
603 $pool addfileprovides
604 $pool createwhatprovides
606 array set cmdactionmap [ list \
607 install $solv::Job_SOLVER_INSTALL \
608 erase $solv::Job_SOLVER_ERASE \
609 up $solv::Job_SOLVER_UPDATE \
610 dup $solv::Job_SOLVER_DISTUPGRADE \
611 verify $solv::Job_SOLVER_VERIFY \
617 foreach arg $::argv {
618 set flags [expr $solv::Selection_SELECTION_NAME | $solv::Selection_SELECTION_PROVIDES | $solv::Selection_SELECTION_GLOB | \
619 $solv::Selection_SELECTION_CANON | $solv::Selection_SELECTION_DOTARCH | $solv::Selection_SELECTION_REL ]
620 switch -glob -- $arg {
622 set flags [expr $flags | $solv::Selection_SELECTION_FILELIST ]
623 if {$cmd eq "erase"} {
624 set flags [expr $flags | $solv::Selection_SELECTION_INSTALLED_ONLY ]
628 set sel [$pool select $arg $flags]
630 set sel [$pool select $arg [expr $flags | $solv::Selection_SELECTION_NOCASE]]
631 if {![$sel isempty]} {
632 puts "\[ignoring case for '$arg']"
636 puts "nothing matches '$arg'"
639 if {[$sel cget -flags] & $solv::Selection_SELECTION_FILELIST} {
640 puts "\[using file list match for '$arg']"
642 if {[$sel cget -flags] & $solv::Selection_SELECTION_PROVIDES} {
643 puts "\[using capability match for '$arg']"
645 lappend jobs {*}[$sel jobs $cmdactionmap($cmd)]
648 if {$jobs eq {} && ($cmd eq "up" || $cmd eq "dup" || $cmd eq "verify") } {
649 set sel [$pool Selection_all]
650 lappend jobs {*}[$sel jobs $cmdactionmap($cmd)]
654 puts "no package matched."
658 if {$cmd eq "list" || $cmd eq "info"} {
660 foreach s [$job solvables] {
661 if {$cmd eq "info"} {
662 puts [format {Name: %s} [$s str]]
663 puts [format {Repo: %s} [[$s cget -repo] cget -name]]
664 puts [format {Summary: %s} [$s lookup_str $solv::SOLVABLE_SUMMARY]]
665 set str [$s lookup_str $solv::SOLVABLE_URL]
667 puts [format {Url: %s} $str]
669 set str [$s lookup_str $solv::SOLVABLE_LICENSE]
671 puts [format {License %s} $str]
673 puts [format {Description: %s} [$s lookup_str $solv::SOLVABLE_DESCRIPTION]]
676 puts [format { - %s [%s]} [$s str] [[$s cget -repo] cget -name]]
677 puts [format { %s} [$s lookup_str $solv::SOLVABLE_SUMMARY]]
684 #$pool set_debuglevel 1
685 set solver [$pool Solver]
686 $solver set_flag $solv::Solver_SOLVER_FLAG_SPLITPROVIDES 1
687 if {$cmd eq "erase"} {
688 $solver set_flag $solv::Solver_SOLVER_FLAG_ALLOW_UNINSTALL 1
691 set problems [$solver solve $jobs]
692 if {$problems ne {}} {
694 foreach problem $problems {
695 puts [format {Problem %d/%d:} $pcnt [llength $problems]]
702 set trans [$solver transaction]
704 if [$trans isempty] {
705 puts "Nothing to do."
710 puts "Transaction summary:"
712 foreach cl [$trans classify [expr $solv::Transaction_SOLVER_TRANSACTION_SHOW_OBSOLETES | $solv::Transaction_SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE]] {
713 switch -- [$cl cget -type] \
714 $solv::Transaction_SOLVER_TRANSACTION_ERASE {
715 puts [format {%d erased packages:} [$cl cget -count]]
717 $solv::Transaction_SOLVER_TRANSACTION_INSTALL {
718 puts [format {%d installed packages:} [$cl cget -count]]
720 $solv::Transaction_SOLVER_TRANSACTION_REINSTALLED {
721 puts [format {%d reinstalled packages:} [$cl cget -count]]
723 $solv::Transaction_SOLVER_TRANSACTION_DOWNGRADED {
724 puts [format {%d downgraded packages:} [$cl cget -count]]
726 $solv::Transaction_SOLVER_TRANSACTION_CHANGED {
727 puts [format {%d changed packages:} [$cl cget -count]]
729 $solv::Transaction_SOLVER_TRANSACTION_UPGRADED {
730 puts [format {%d upgraded packages:} [$cl cget -count]]
732 $solv::Transaction_SOLVER_TRANSACTION_VENDORCHANGE {
733 puts [format {%d vendor changes from '%s' to '%s':} [$cl cget -count] [$cl cget -fromstr] [$cl cget -tostr]]
735 $solv::Transaction_SOLVER_TRANSACTION_ARCHCHANGE {
736 puts [format {%d archchanges from '%s' to '%s':} [$cl cget -count] [$cl cget -fromstr] [$cl cget -tostr]]
739 foreach p [$cl solvables] {
740 set cltype [$cl cget -type]
741 if {$cltype == $solv::Transaction_SOLVER_TRANSACTION_UPGRADED || $cltype ==$solv::Transaction_SOLVER_TRANSACTION_DOWNGRADED} {
742 set op [$trans othersolvable $p]
743 puts [format { - %s -> %s} [$p str] [$op str]]
745 puts [format { - %s} [$p str]]
750 puts [format {install size change: %d K} [$trans calc_installsizechange]]
754 puts -nonewline "OK to continue (y/n)? "
760 if {$yn eq "n" || $yn eq "q"} {
765 set newpkgs [$trans newsolvables]
766 array set newpkgs_f {}
767 if {$newpkgs ne {}} {
770 set downloadsize [expr $downloadsize + [$p lookup_num $solv::SOLVABLE_DOWNLOADSIZE]]
772 puts [format {Downloading %d packages, %d K} [llength $newpkgs] [expr $downloadsize / 1024]]
774 upvar #0 [[$p cget -repo] cget -appdata] repo
775 set location [$p lookup_location]
776 if {$location eq {}} {
779 set location "[repo_packagespath repo][lindex $location 0]"
780 set checksum [$p lookup_checksum $solv::SOLVABLE_CHECKSUM]
781 set f [repo_download repo $location 0 $checksum]
782 set newpkgs_f([$p cget -id]) $f
789 puts "Committing transaction:"
791 foreach p [$trans steps] {
792 set steptype [$trans steptype $p $solv::Transaction_SOLVER_TRANSACTION_RPM_ONLY]
793 if {$steptype == $solv::Transaction_SOLVER_TRANSACTION_ERASE} {
794 puts "erase [$p str]"
795 regsub {^[0-9]+:} [$p cget -evr] {} nvr
796 set nvr "[$p cget -name]-$nvr.[$p cget -arch]"
797 exec -ignorestderr -- rpm -e --nodeps --nodigest --nosignature $nvr
798 } elseif {$steptype == $solv::Transaction_SOLVER_TRANSACTION_INSTALL || $steptype == $solv::Transaction_SOLVER_TRANSACTION_MULTIINSTALL} {
799 puts "install [$p str]"
800 set f $newpkgs_f([$p cget -id])
801 set mode [expr {$steptype == $solv::Transaction_SOLVER_TRANSACTION_INSTALL ? "-U" : "-i"}]
803 exec -ignorestderr -- rpm $mode --force --nodeps --nodigest --nosignature "/dev/fd/[$f fileno]"