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 repo_repomd_add_ext self $repodata "filelists" "FL"
264 $repodata internalize
267 proc repo_repomd_find {selfName what} {
269 set di [$self(handle) Dataiterator_meta $solv::REPOSITORY_REPOMD_TYPE $what $solv::Dataiterator_SEARCH_STRING]
270 $di prepend_keyname $solv::REPOSITORY_REPOMD
272 set dp [$d parentpos]
273 set filename [$dp lookup_str $solv::REPOSITORY_REPOMD_LOCATION]
274 set checksum [$dp lookup_checksum $solv::REPOSITORY_REPOMD_CHECKSUM]
275 if {$filename ne "" && $checksum eq "NULL"} {
276 puts "no $filename file checksum"
277 } elseif {$filename ne ""} {
278 return [list $filename $checksum]
284 proc repo_repomd_load {selfName pool} {
286 if [repo_generic_load self $pool] {
289 puts -nonewline "rpmmd repo '$self(name)': "
290 set f [repo_download self {repodata/repomd.xml} 0 {}]
292 puts "no repomd.xml file, skipped"
293 repo_free_handle self
296 set self(cookie) [repo_calc_cookie_fp self $f]
297 if [repo_usecachedrepo self "-" 1] {
301 set handle $self(handle)
302 $handle add_repomdxml $f
304 set primary [repo_repomd_find self primary]
305 if {$primary ne {}} {
306 set f [repo_download self [lindex $primary 0] 1 [lindex $primary 1] 1]
308 $handle add_rpmmd $f {}
311 if [info exists self(incomplete)] {
315 set updateinfo [repo_repomd_find self primary]
316 if {$updateinfo ne {}} {
317 set f [repo_download self [lindex $updateinfo 0] 1 [lindex $updateinfo 1] 1]
319 $handle add_updateinfoxml $f
323 repo_repomd_add_exts self
324 repo_writecachedrepo self
325 $self(handle) create_stubs
329 proc repo_repomd_packagespath {selfName} {
333 proc repo_repomd_load_ext {selfName repodata} {
335 switch -- [$repodata lookup_str $solv::SOLVID_META $solv::REPOSITORY_REPOMD_TYPE] {
346 puts -nonewline "\[$self(name):$ext: "
348 if [repo_usecachedrepo self $ext] {
353 set handle $self(handle)
354 set filename [$repodata lookup_str $solv::SOLVID_META $solv::REPOSITORY_REPOMD_LOCATION]
355 set filechecksum [$repodata lookup_checksum $solv::SOLVID_META $solv::REPOSITORY_REPOMD_CHECKSUM]
356 set f [repo_download self $filename 1 $filechecksum]
361 $handle add_rpmmd $f "FL" [ expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES | $solv::Repo_REPO_LOCALPOOL]
364 repo_writecachedrepo self $ext $repodata
370 proc repo_susetags_add_ext {selfName repodata what ext} {
372 set where [repo_susetags_find self $what]
376 set h [$repodata new_handle]
377 $repodata set_str $h $solv::SUSETAGS_FILE_NAME [lindex $where 0]
378 $repodata set_checksum $h $solv::SUSETAGS_FILE_CHECKSUM [lindex $where 1]
379 repo_generic_add_ext_keys self $ext $repodata $h
380 $repodata add_flexarray $solv::SOLVID_META $solv::REPOSITORY_EXTERNAL $h
383 proc repo_susetags_add_exts {selfName} {
385 set repodata [$self(handle) add_repodata 0]
386 repo_susetags_add_ext self $repodata "packages.FL" "FL"
387 repo_susetags_add_ext self $repodata "packages.FL.gz" "FL"
388 $repodata internalize
391 proc repo_susetags_find {selfName what} {
393 set di [$self(handle) Dataiterator_meta $solv::SUSETAGS_FILE_NAME $what $solv::Dataiterator_SEARCH_STRING]
394 $di prepend_keyname $solv::SUSETAGS_FILE
396 set dp [$d parentpos]
397 set checksum [$dp lookup_checksum $solv::SUSETAGS_FILE_CHECKSUM]
398 return [list $what $checksum]
403 proc repo_susetags_load {selfName pool} {
405 if [repo_generic_load self $pool] {
408 puts -nonewline "susetags repo '$self(name)': "
409 set f [repo_download self {content} 0 {}]
411 puts "no content file, skipped"
412 repo_free_handle self
415 set self(cookie) [repo_calc_cookie_fp self $f]
416 if [repo_usecachedrepo self "-" 1] {
420 set handle $self(handle)
421 $handle add_content $f
423 set defvendorid [[$handle cget -meta] lookup_id $solv::SUSETAGS_DEFAULTVENDOR]
424 set descrdir [[$handle cget -meta] lookup_str $solv::SUSETAGS_DESCRDIR]
425 if {$descrdir eq {NULL}} {
426 set descrdir "suse/setup/descr"
428 set packages [repo_susetags_find self "packages.gz"]
429 if {$packages eq {}} {
430 set packages [repo_susetags_find self "packages"]
432 if {$packages ne {}} {
433 set f [repo_download self "$descrdir/[lindex $packages 0]" 1 [lindex $packages 1] 1]
435 $handle add_susetags $f $defvendorid {} [expr $solv::Repo_REPO_NO_INTERNALIZE | $solv::Repo_SUSETAGS_RECORD_SHARES]
437 set packages [repo_susetags_find self "packages.en.gz"]
438 if {$packages eq {}} {
439 set packages [repo_susetags_find self "packages.en"]
441 if {$packages ne {}} {
442 set f [repo_download self "$descrdir/[lindex $packages 0]" 1 [lindex $packages 1] 1]
444 $handle add_susetags $f $defvendorid {} [expr $solv::Repo_REPO_NO_INTERNALIZE | $solv::Repo_REPO_REUSE_REPODATA | $solv::Repo_REPO_EXTEND_SOLVABLES ]
451 repo_susetags_add_exts self
452 repo_writecachedrepo self
453 $self(handle) create_stubs
457 proc repo_susetags_packagespath {selfName} {
459 set datadir [[$self(handle) cget -meta] lookup_str $solv::SUSETAGS_DATADIR]
460 return [expr {$datadir ne {} ? "$datadir/" : "suse/"}]
463 proc repo_susetags_load_ext {selfName repodata} {
465 set filename [$repodata lookup_str $solv::SOLVID_META $solv::SUSETAGS_FILE_NAME]
466 set ext [string range $filename 9 10]
467 puts -nonewline "\[$self(name):$ext: "
469 if [repo_usecachedrepo self $ext] {
474 set handle $self(handle)
475 set defvendorid [[$handle cget -meta] lookup_id $solv::SUSETAGS_DEFAULTVENDOR]
476 set descrdir [[$handle cget -meta] lookup_str $solv::SUSETAGS_DESCRDIR]
477 if {$descrdir eq {NULL}} {
478 set descrdir "suse/setup/descr"
480 set filechecksum [$repodata lookup_checksum $solv::SOLVID_META $solv::SUSETAGS_FILE_CHECKSUM]
481 set f [repo_download self "$descrdir/$filename" 1 $filechecksum]
485 set flags [expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES]
487 set flags [expr $flags | $solv::Repo_REPO_LOCALPOOL]
489 $handle add_susetags $f $defvendorid $ext $flags
491 repo_writecachedrepo self $ext $repodata
497 proc repo_unknown_load {selfName pool} {
499 puts "unsupported repo '$self(name)': skipped"
505 proc repo_load {selfName pool} {
507 "repo_$self(type)_load" self $pool
510 proc repo_packagespath {selfName} {
512 "repo_$self(type)_packagespath" self
515 proc repo_load_ext {selfName repodata} {
517 "repo_$self(type)_load_ext" self $repodata
522 proc load_stub {repodata} {
524 upvar #0 [[$repodata cget -repo] cget -appdata] repo
525 if [info exists repo(handle)] {
526 return [repo_load_ext repo $repodata]
540 foreach reponame [lsort [glob -nocomplain -directory $reposdir *.repo]] {
541 set ini [::ini::open $reponame r]
542 foreach alias [::ini::sections $ini] {
543 upvar #0 [globalarray] repo
544 array set repo {enabled 0 priority 99 autorefresh 1 type rpm-md metadata_expire 900}
545 array set repo [::ini::get $ini $alias]
546 set repo(name) $alias
547 switch -exact -- $repo(type) {
548 rpm-md { set repo(type) repomd }
549 yast2 { set repo(type) susetags }
550 default { set repo(type) unknown }
552 lappend repoNames $repo(varName)
557 set pool [solv::new_Pool]
559 $pool set_loadcallback load_stub
561 upvar #0 [globalarray] sysrepo
562 array set sysrepo [list name {@System} type system]
563 repo_load sysrepo $pool
565 foreach repoName $repoNames {
566 upvar 0 $repoName repo
567 if {$repo(enabled)} {
573 set cmd [lindex $::argv 0]
574 set ::argv [lreplace $::argv 0 0]
576 array set cmdabbrev [ list \
583 if [info exists cmdabbrev($cmd)] {
584 set cmd $cmdabbrev($cmd)
587 if {$cmd eq "search"} {
588 set arg [lindex $::argv 0]
589 $pool createwhatprovides
590 set sel [$pool Selection]
591 set di [$pool Dataiterator $solv::SOLVABLE_NAME $arg [ expr $solv::Dataiterator_SEARCH_SUBSTRING | $solv::Dataiterator_SEARCH_NOCASE ]]
593 $sel add_raw $solv::Job_SOLVER_SOLVABLE [$d cget -solvid]
595 foreach s [$sel solvables] {
596 puts [format { - %s [%s]: %s} [$s str] [[$s cget -repo] cget -name] [$s lookup_str $solv::SOLVABLE_SUMMARY]]
601 $pool addfileprovides
602 $pool createwhatprovides
604 array set cmdactionmap [ list \
605 install $solv::Job_SOLVER_INSTALL \
606 erase $solv::Job_SOLVER_ERASE \
607 up $solv::Job_SOLVER_UPDATE \
608 dup $solv::Job_SOLVER_DISTUPGRADE \
609 verify $solv::Job_SOLVER_VERIFY \
615 foreach arg $::argv {
616 set flags [expr $solv::Selection_SELECTION_NAME | $solv::Selection_SELECTION_PROVIDES | $solv::Selection_SELECTION_GLOB | \
617 $solv::Selection_SELECTION_CANON | $solv::Selection_SELECTION_DOTARCH | $solv::Selection_SELECTION_REL ]
618 switch -glob -- $arg {
620 set flags [expr $flags | $solv::Selection_SELECTION_FILELIST ]
621 if {$cmd eq "erase"} {
622 set flags [expr $flags | $solv::Selection_SELECTION_INSTALLED_ONLY ]
626 set sel [$pool select $arg $flags]
628 set sel [$pool select $arg [expr $flags | $solv::Selection_SELECTION_NOCASE]]
629 if {![$sel isempty]} {
630 puts "\[ignoring case for '$arg']"
634 puts "nothing matches '$arg'"
637 if {[$sel flags] & $solv::Selection_SELECTION_FILELIST} {
638 puts "\[using file list match for '$arg']"
640 if {[$sel flags] & $solv::Selection_SELECTION_PROVIDES} {
641 puts "\[using capability match for '$arg']"
643 lappend jobs {*}[$sel jobs $cmdactionmap($cmd)]
646 if {$jobs eq {} && ($cmd eq "up" || $cmd eq "dup" || $cmd eq "verify") } {
647 set sel [$pool Selection_all]
648 lappend jobs {*}[$sel jobs $cmdactionmap($cmd)]
652 puts "no package matched."
656 if {$cmd eq "list" || $cmd eq "info"} {
658 foreach s [$job solvables] {
659 if {$cmd eq "info"} {
660 puts [format {Name: %s} [$s str]]
661 puts [format {Repo: %s} [[$s cget -repo] cget -name]]
662 puts [format {Summary: %s} [$s lookup_str $solv::SOLVABLE_SUMMARY]]
663 set str [$s lookup_str $solv::SOLVABLE_URL]
665 puts [format {Url: %s} $str]
667 set str [$s lookup_str $solv::SOLVABLE_LICENSE]
669 puts [format {License %s} $str]
671 puts [format {Description: %s} [$s lookup_str $solv::SOLVABLE_DESCRIPTION]]
674 puts [format { - %s [%s]} [$s str] [[$s cget -repo] cget -name]]
675 puts [format { %s} [$s lookup_str $solv::SOLVABLE_SUMMARY]]
682 #$pool set_debuglevel 1
683 set solver [$pool Solver]
684 $solver set_flag $solv::Solver_SOLVER_FLAG_SPLITPROVIDES 1
685 if {$cmd eq "erase"} {
686 $solver set_flag $solv::Solver_SOLVER_FLAG_ALLOW_UNINSTALL 1
689 set problems [$solver solve $jobs]
690 if {$problems ne {}} {
692 foreach problem $problems {
693 puts [format {Problem %d/%d:} $pcnt [llength $problems]]
700 set trans [$solver transaction]
702 if [$trans isempty] {
703 puts "Nothing to do."
708 puts "Transaction summary:"
710 foreach cl [$trans classify [expr $solv::Transaction_SOLVER_TRANSACTION_SHOW_OBSOLETES | $solv::Transaction_SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE]] {
711 switch -- [$cl cget -type] \
712 $solv::Transaction_SOLVER_TRANSACTION_ERASE {
713 puts [format {%d erased packages:} [$cl cget -count]]
715 $solv::Transaction_SOLVER_TRANSACTION_INSTALL {
716 puts [format {%d installed packages:} [$cl cget -count]]
718 $solv::Transaction_SOLVER_TRANSACTION_REINSTALLED {
719 puts [format {%d reinstalled packages:} [$cl cget -count]]
721 $solv::Transaction_SOLVER_TRANSACTION_DOWNGRADED {
722 puts [format {%d downgraded packages:} [$cl cget -count]]
724 $solv::Transaction_SOLVER_TRANSACTION_CHANGED {
725 puts [format {%d changed packages:} [$cl cget -count]]
727 $solv::Transaction_SOLVER_TRANSACTION_UPGRADED {
728 puts [format {%d upgraded packages:} [$cl cget -count]]
730 $solv::Transaction_SOLVER_TRANSACTION_VENDORCHANGE {
731 puts [format {%d vendor changes from '%s' to '%s':} [$cl cget -count] [$cl cget -fromstr] [$cl cget -tostr]]
733 $solv::Transaction_SOLVER_TRANSACTION_ARCHCHANGE {
734 puts [format {%d archchanges from '%s' to '%s':} [$cl cget -count] [$cl cget -fromstr] [$cl cget -tostr]]
737 foreach p [$cl solvables] {
738 set cltype [$cl cget -type]
739 if {$cltype == $solv::Transaction_SOLVER_TRANSACTION_UPGRADED || $cltype ==$solv::Transaction_SOLVER_TRANSACTION_DOWNGRADED} {
740 set op [$trans othersolvable $p]
741 puts [format { - %s -> %s} [$p str] [$op str]]
743 puts [format { - %s} [$p str]]
748 puts [format {install size change: %d K} [$trans calc_installsizechange]]
752 puts -nonewline "OK to continue (y/n)? "
758 if {$yn eq "n" || $yn eq "q"} {
763 set newpkgs [$trans newsolvables]
764 array set newpkgs_f {}
765 if {$newpkgs ne {}} {
768 set downloadsize [expr $downloadsize + [$p lookup_num $solv::SOLVABLE_DOWNLOADSIZE]]
770 puts [format {Downloading %d packages, %d K} [llength $newpkgs] [expr $downloadsize / 1024]]
772 upvar #0 [[$p cget -repo] cget -appdata] repo
773 set location [$p lookup_location]
774 if {$location eq {}} {
777 set location "[repo_packagespath repo][lindex $location 0]"
778 set checksum [$p lookup_checksum $solv::SOLVABLE_CHECKSUM]
779 set f [repo_download repo $location 0 $checksum]
780 set newpkgs_f([$p cget -id]) $f
787 puts "Committing transaction:"
789 foreach p [$trans steps] {
790 set steptype [$trans steptype $p $solv::Transaction_SOLVER_TRANSACTION_RPM_ONLY]
791 if {$steptype == $solv::Transaction_SOLVER_TRANSACTION_ERASE} {
792 puts "erase [$p str]"
793 regsub {^[0-9]+:} [$p cget -evr] {} nvr
794 set nvr "[$p cget -name]-$nvr.[$p cget -arch]"
795 exec -ignorestderr -- rpm -e --nodeps --nodigest --nosignature $nvr
796 } elseif {$steptype == $solv::Transaction_SOLVER_TRANSACTION_INSTALL || $steptype == $solv::Transaction_SOLVER_TRANSACTION_MULTIINSTALL} {
797 puts "install [$p str]"
798 set f $newpkgs_f([$p cget -id])
799 set mode [expr {$steptype == $solv::Transaction_SOLVER_TRANSACTION_INSTALL ? "-U" : "-i"}]
801 exec -ignorestderr -- rpm $mode --force --nodeps --nodigest --nosignature "/dev/fd/[$f fileno]"