1 // -*- mode: cpp; mode: fold -*- 
   3 /* ##################################################################### 
   4    apt-mark - show and change auto-installed bit information 
   5    ##################################################################### */ 
   7 // Include Files                                                        /*{{{*/ 
  10 #include <apt-pkg/cachefile.h> 
  11 #include <apt-pkg/cacheset.h> 
  12 #include <apt-pkg/cmndline.h> 
  13 #include <apt-pkg/error.h> 
  14 #include <apt-pkg/init.h> 
  15 #include <apt-pkg/strutl.h> 
  16 #include <apt-pkg/pkgsystem.h> 
  17 #include <apt-pkg/fileutl.h> 
  22 #include <sys/types.h> 
  27 #include <apt-private/private-cmndline.h> 
  36 ofstream 
devnull("/dev/null"); 
  37 /* DoAuto - mark packages as automatically/manually installed           {{{*/ 
  38 bool DoAuto(CommandLine 
&CmdL
) 
  40    pkgCacheFile CacheFile
; 
  41    pkgCache 
*Cache 
= CacheFile
.GetPkgCache(); 
  42    pkgDepCache 
*DepCache 
= CacheFile
.GetDepCache(); 
  43    if (unlikely(Cache 
== NULL 
|| DepCache 
== NULL
)) 
  46    APT::PackageList pkgset 
= APT::PackageList::FromCommandLine(CacheFile
, CmdL
.FileList 
+ 1); 
  47    if (pkgset
.empty() == true) 
  48       return _error
->Error(_("No packages found")); 
  50    bool MarkAuto 
= strcasecmp(CmdL
.FileList
[0],"auto") == 0; 
  51    int AutoMarkChanged 
= 0; 
  53    for (APT::PackageList::const_iterator Pkg 
= pkgset
.begin(); Pkg 
!= pkgset
.end(); ++Pkg
) 
  55       if (Pkg
->CurrentVer 
== 0) 
  57          ioprintf(c1out
,_("%s can not be marked as it is not installed.\n"), Pkg
.FullName(true).c_str()); 
  60       else if ((((*DepCache
)[Pkg
].Flags 
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
) == MarkAuto
) 
  62          if (MarkAuto 
== false) 
  63             ioprintf(c1out
,_("%s was already set to manually installed.\n"), Pkg
.FullName(true).c_str()); 
  65             ioprintf(c1out
,_("%s was already set to automatically installed.\n"), Pkg
.FullName(true).c_str()); 
  69       if (MarkAuto 
== false) 
  70          ioprintf(c1out
,_("%s set to manually installed.\n"), Pkg
.FullName(true).c_str()); 
  72          ioprintf(c1out
,_("%s set to automatically installed.\n"), Pkg
.FullName(true).c_str()); 
  74       DepCache
->MarkAuto(Pkg
, MarkAuto
); 
  77    if (AutoMarkChanged 
> 0 && _config
->FindB("APT::Mark::Simulate", false) == false) 
  78       return DepCache
->writeStateFile(NULL
); 
  82 /* DoMarkAuto - mark packages as automatically/manually installed       {{{*/ 
  83 /* Does the same as DoAuto but tries to do it exactly the same why as 
  84    the python implementation did it so it can be a drop-in replacement */ 
  85 bool DoMarkAuto(CommandLine 
&CmdL
) 
  87    pkgCacheFile CacheFile
; 
  88    pkgCache 
*Cache 
= CacheFile
.GetPkgCache(); 
  89    pkgDepCache 
*DepCache 
= CacheFile
.GetDepCache(); 
  90    if (unlikely(Cache 
== NULL 
|| DepCache 
== NULL
)) 
  93    APT::PackageList pkgset 
= APT::PackageList::FromCommandLine(CacheFile
, CmdL
.FileList 
+ 1); 
  94    if (pkgset
.empty() == true) 
  95       return _error
->Error(_("No packages found")); 
  97    bool const MarkAuto 
= strcasecmp(CmdL
.FileList
[0],"markauto") == 0; 
  98    bool const Verbose 
= _config
->FindB("APT::MarkAuto::Verbose", false); 
  99    int AutoMarkChanged 
= 0; 
 101    for (APT::PackageList::const_iterator Pkg 
= pkgset
.begin(); Pkg 
!= pkgset
.end(); ++Pkg
) 
 103       if (Pkg
->CurrentVer 
== 0 || 
 104           (((*DepCache
)[Pkg
].Flags 
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
) == MarkAuto
) 
 108          ioprintf(c1out
, "changing %s to %d\n", Pkg
.Name(), (MarkAuto 
== false) ? 0 : 1); 
 110       DepCache
->MarkAuto(Pkg
, MarkAuto
); 
 113    if (AutoMarkChanged 
> 0 && _config
->FindB("APT::Mark::Simulate", false) == false) 
 114       return DepCache
->writeStateFile(NULL
); 
 116    _error
->Notice(_("This command is deprecated. Please use 'apt-mark auto' and 'apt-mark manual' instead.")); 
 121 /* ShowAuto - show automatically installed packages (sorted)            {{{*/ 
 122 bool ShowAuto(CommandLine 
&CmdL
) 
 124    pkgCacheFile CacheFile
; 
 125    pkgCache 
*Cache 
= CacheFile
.GetPkgCache(); 
 126    pkgDepCache 
*DepCache 
= CacheFile
.GetDepCache(); 
 127    if (unlikely(Cache 
== NULL 
|| DepCache 
== NULL
)) 
 130    std::vector
<string
> packages
; 
 132    bool const ShowAuto 
= strcasecmp(CmdL
.FileList
[0],"showauto") == 0; 
 134    if (CmdL
.FileList
[1] == 0) 
 136       packages
.reserve(Cache
->HeaderP
->PackageCount 
/ 3); 
 137       for (pkgCache::PkgIterator P 
= Cache
->PkgBegin(); P
.end() == false; ++P
) 
 138          if (P
->CurrentVer 
!= 0 && 
 139              (((*DepCache
)[P
].Flags 
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
) == ShowAuto
) 
 140             packages
.push_back(P
.FullName(true)); 
 144       APT::CacheSetHelper 
helper(false); // do not show errors 
 145       APT::PackageSet pkgset 
= APT::PackageSet::FromCommandLine(CacheFile
, CmdL
.FileList 
+ 1, helper
); 
 146       packages
.reserve(pkgset
.size()); 
 147       for (APT::PackageSet::const_iterator P 
= pkgset
.begin(); P 
!= pkgset
.end(); ++P
) 
 148          if (P
->CurrentVer 
!= 0 && 
 149              (((*DepCache
)[P
].Flags 
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
) == ShowAuto
) 
 150             packages
.push_back(P
.FullName(true)); 
 153    std::sort(packages
.begin(), packages
.end()); 
 155    for (vector
<string
>::const_iterator I 
= packages
.begin(); I 
!= packages
.end(); ++I
) 
 156       std::cout 
<< *I 
<< std::endl
; 
 161 /* DoHold - mark packages as hold by dpkg                               {{{*/ 
 162 bool DoHold(CommandLine 
&CmdL
) 
 164    pkgCacheFile CacheFile
; 
 165    pkgCache 
*Cache 
= CacheFile
.GetPkgCache(); 
 166    if (unlikely(Cache 
== NULL
)) 
 169    // Generate the base argument list for dpkg 
 170    std::vector
<const char *> Args
; 
 171    string Tmp 
= _config
->Find("Dir::Bin::dpkg","dpkg"); 
 173       string 
const dpkgChrootDir 
= _config
->FindDir("DPkg::Chroot-Directory", "/"); 
 174       size_t dpkgChrootLen 
= dpkgChrootDir
.length(); 
 175       if (dpkgChrootDir 
!= "/" && Tmp
.find(dpkgChrootDir
) == 0) 
 177          if (dpkgChrootDir
[dpkgChrootLen 
- 1] == '/') 
 179          Tmp 
= Tmp
.substr(dpkgChrootLen
); 
 182    Args
.push_back(Tmp
.c_str()); 
 184    // Stick in any custom dpkg options 
 185    Configuration::Item 
const *Opts 
= _config
->Tree("DPkg::Options"); 
 189       for (; Opts 
!= 0; Opts 
= Opts
->Next
) 
 191          if (Opts
->Value
.empty() == true) 
 193          Args
.push_back(Opts
->Value
.c_str()); 
 197    size_t const BaseArgs 
= Args
.size(); 
 198    // we need to detect if we can qualify packages with the architecture or not 
 199    Args
.push_back("--assert-multi-arch"); 
 200    Args
.push_back(NULL
); 
 203    pid_t dpkgAssertMultiArch 
= ExecFork(); 
 204    if (dpkgAssertMultiArch 
== 0) 
 206       std::string 
const chrootDir 
= _config
->FindDir("DPkg::Chroot-Directory"); 
 207       // redirect everything to the ultimate sink as we only need the exit-status 
 208       int const nullfd 
= open("/dev/null", O_RDONLY
); 
 209       dup2(nullfd
, STDIN_FILENO
); 
 210       dup2(nullfd
, STDOUT_FILENO
); 
 211       dup2(nullfd
, STDERR_FILENO
); 
 212       if (chrootDir 
!= "/" && chroot(chrootDir
.c_str()) != 0 && chdir("/") != 0) 
 213          _error
->WarningE("getArchitecture", "Couldn't chroot into %s for dpkg --assert-multi-arch", chrootDir
.c_str()); 
 214       execvp(Args
[0], (char**) &Args
[0]); 
 215       _error
->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!"); 
 219    APT::PackageList pkgset 
= APT::PackageList::FromCommandLine(CacheFile
, CmdL
.FileList 
+ 1); 
 220    if (pkgset
.empty() == true) 
 221       return _error
->Error(_("No packages found")); 
 223    bool const MarkHold 
= strcasecmp(CmdL
.FileList
[0],"hold") == 0; 
 225    for (APT::PackageList::iterator Pkg 
= pkgset
.begin(); Pkg 
!= pkgset
.end();) 
 227       if ((Pkg
->SelectedState 
== pkgCache::State::Hold
) == MarkHold
) 
 229          if (MarkHold 
== true) 
 230             ioprintf(c1out
,_("%s was already set on hold.\n"), Pkg
.FullName(true).c_str()); 
 232             ioprintf(c1out
,_("%s was already not hold.\n"), Pkg
.FullName(true).c_str()); 
 233          Pkg 
= pkgset
.erase(Pkg
, true); 
 239    bool dpkgMultiArch 
= false; 
 240    if (dpkgAssertMultiArch 
> 0) 
 243       while (waitpid(dpkgAssertMultiArch
, &Status
, 0) != dpkgAssertMultiArch
) 
 247          _error
->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch"); 
 250       if (WIFEXITED(Status
) == true && WEXITSTATUS(Status
) == 0) 
 251          dpkgMultiArch 
= true; 
 254    if (pkgset
.empty() == true) 
 257    if (_config
->FindB("APT::Mark::Simulate", false) == true) 
 259       for (APT::PackageList::iterator Pkg 
= pkgset
.begin(); Pkg 
!= pkgset
.end(); ++Pkg
) 
 261          if (MarkHold 
== false) 
 262             ioprintf(c1out
,_("%s set on hold.\n"), Pkg
.FullName(true).c_str()); 
 264             ioprintf(c1out
,_("Canceled hold on %s.\n"), Pkg
.FullName(true).c_str()); 
 269    Args
.erase(Args
.begin() + BaseArgs
, Args
.end()); 
 270    Args
.push_back("--set-selections"); 
 271    Args
.push_back(NULL
); 
 273    int external
[2] = {-1, -1}; 
 274    if (pipe(external
) != 0) 
 275       return _error
->WarningE("DoHold", "Can't create IPC pipe for dpkg --set-selections"); 
 277    pid_t dpkgSelection 
= ExecFork(); 
 278    if (dpkgSelection 
== 0) 
 281       std::string 
const chrootDir 
= _config
->FindDir("DPkg::Chroot-Directory"); 
 282       if (chrootDir 
!= "/" && chroot(chrootDir
.c_str()) != 0 && chdir("/") != 0) 
 283          _error
->WarningE("getArchitecture", "Couldn't chroot into %s for dpkg --set-selections", chrootDir
.c_str()); 
 284       int const nullfd 
= open("/dev/null", O_RDONLY
); 
 285       dup2(external
[0], STDIN_FILENO
); 
 286       dup2(nullfd
, STDOUT_FILENO
); 
 287       dup2(nullfd
, STDERR_FILENO
); 
 288       execvp(Args
[0], (char**) &Args
[0]); 
 289       _error
->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!"); 
 293    FILE* dpkg 
= fdopen(external
[1], "w"); 
 294    for (APT::PackageList::iterator Pkg 
= pkgset
.begin(); Pkg 
!= pkgset
.end(); ++Pkg
) 
 296       if (dpkgMultiArch 
== false) 
 297          fprintf(dpkg
, "%s", Pkg
.FullName(true).c_str()); 
 300          if (Pkg
->CurrentVer 
!= 0) 
 301             fprintf(dpkg
, "%s:%s", Pkg
.Name(), Pkg
.CurrentVer().Arch()); 
 302          else if (Pkg
.VersionList().end() == false) 
 303             fprintf(dpkg
, "%s:%s", Pkg
.Name(), Pkg
.VersionList().Arch()); 
 305             fprintf(dpkg
, "%s", Pkg
.FullName(false).c_str()); 
 308       if (MarkHold 
== true) 
 310          fprintf(dpkg
, " hold\n"); 
 311          ioprintf(c1out
,_("%s set on hold.\n"), Pkg
.FullName(true).c_str()); 
 315          fprintf(dpkg
, " install\n"); 
 316          ioprintf(c1out
,_("Canceled hold on %s.\n"), Pkg
.FullName(true).c_str()); 
 321    if (dpkgSelection 
> 0) 
 324       while (waitpid(dpkgSelection
, &Status
, 0) != dpkgSelection
) 
 328          _error
->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --set-selection"); 
 331       if (WIFEXITED(Status
) == true && WEXITSTATUS(Status
) == 0) 
 334    return _error
->Error(_("Executing dpkg failed. Are you root?")); 
 337 /* ShowHold - show packages set on hold in dpkg status                  {{{*/ 
 338 bool ShowHold(CommandLine 
&CmdL
) 
 340    pkgCacheFile CacheFile
; 
 341    pkgCache 
*Cache 
= CacheFile
.GetPkgCache(); 
 342    if (unlikely(Cache 
== NULL
)) 
 345    std::vector
<string
> packages
; 
 347    if (CmdL
.FileList
[1] == 0) 
 349       packages
.reserve(50); // how many holds are realistic? I hope just a few… 
 350       for (pkgCache::PkgIterator P 
= Cache
->PkgBegin(); P
.end() == false; ++P
) 
 351          if (P
->SelectedState 
== pkgCache::State::Hold
) 
 352             packages
.push_back(P
.FullName(true)); 
 356       APT::CacheSetHelper 
helper(false); // do not show errors 
 357       APT::PackageSet pkgset 
= APT::PackageSet::FromCommandLine(CacheFile
, CmdL
.FileList 
+ 1, helper
); 
 358       packages
.reserve(pkgset
.size()); 
 359       for (APT::PackageSet::const_iterator P 
= pkgset
.begin(); P 
!= pkgset
.end(); ++P
) 
 360          if (P
->SelectedState 
== pkgCache::State::Hold
) 
 361             packages
.push_back(P
.FullName(true)); 
 364    std::sort(packages
.begin(), packages
.end()); 
 366    for (vector
<string
>::const_iterator I 
= packages
.begin(); I 
!= packages
.end(); ++I
) 
 367       std::cout 
<< *I 
<< std::endl
; 
 372 // ShowHelp - Show a help screen                                        /*{{{*/ 
 373 // --------------------------------------------------------------------- 
 375 bool ShowHelp(CommandLine 
&CmdL
) 
 377    ioprintf(cout
,_("%s %s for %s compiled on %s %s\n"),PACKAGE
,PACKAGE_VERSION
, 
 378             COMMON_ARCH
,__DATE__
,__TIME__
); 
 381     _("Usage: apt-mark [options] {auto|manual} pkg1 [pkg2 ...]\n" 
 383       "apt-mark is a simple command line interface for marking packages\n" 
 384       "as manually or automatically installed. It can also list marks.\n" 
 387       "   auto - Mark the given packages as automatically installed\n" 
 388       "   manual - Mark the given packages as manually installed\n" 
 391       "  -h  This help text.\n" 
 392       "  -q  Loggable output - no progress indicator\n" 
 393       "  -qq No output except for errors\n" 
 394       "  -s  No-act. Just prints what would be done.\n" 
 395       "  -f  read/write auto/manual marking in the given file\n" 
 396       "  -c=? Read this configuration file\n" 
 397       "  -o=? Set an arbitrary configuration option, eg -o dir::cache=/tmp\n" 
 398       "See the apt-mark(8) and apt.conf(5) manual pages for more information.") 
 403 int main(int argc
,const char *argv
[])                                   /*{{{*/ 
 405    CommandLine::Dispatch Cmds
[] = {{"help",&ShowHelp
}, 
 410                                    {"showauto",&ShowAuto
}, 
 411                                    {"showmanual",&ShowAuto
}, 
 412                                    {"showhold",&ShowHold
}, 
 413                                    // be nice and forgive the typo 
 414                                    {"showholds",&ShowHold
}, 
 415                                    // be nice and forgive it as it is technical right 
 417                                    // obsolete commands for compatibility 
 418                                    {"markauto", &DoMarkAuto
}, 
 419                                    {"unmarkauto", &DoMarkAuto
}, 
 422    std::vector
<CommandLine::Args
> Args 
= getCommandArgs("apt-mark", CommandLine::GetCommand(Cmds
, argc
, argv
)); 
 424    // Set up gettext support 
 425    setlocale(LC_ALL
,""); 
 428    // Parse the command line and initialize the package library 
 429    CommandLine 
CmdL(Args
.data(),_config
); 
 430    if (pkgInitConfig(*_config
) == false || 
 431        CmdL
.Parse(argc
,argv
) == false || 
 432        pkgInitSystem(*_config
,_system
) == false) 
 434       if (_config
->FindB("version") == true) 
 436       _error
->DumpErrors(); 
 440    // See if the help should be shown 
 441    if (_config
->FindB("help") == true || 
 442        _config
->FindB("version") == true || 
 443        CmdL
.FileSize() == 0) 
 449    // Deal with stdout not being a tty 
 450    if (!isatty(STDOUT_FILENO
) && _config
->FindI("quiet", -1) == -1) 
 451       _config
->Set("quiet","1"); 
 453    // Setup the output streams 
 454    c0out
.rdbuf(cout
.rdbuf()); 
 455    c1out
.rdbuf(cout
.rdbuf()); 
 456    c2out
.rdbuf(cout
.rdbuf()); 
 457    if (_config
->FindI("quiet",0) > 0) 
 458       c0out
.rdbuf(devnull
.rdbuf()); 
 459    if (_config
->FindI("quiet",0) > 1) 
 460       c1out
.rdbuf(devnull
.rdbuf()); 
 462    // Match the operation 
 463    CmdL
.DispatchArg(Cmds
); 
 465    // Print any errors or warnings found during parsing 
 466    bool const Errors 
= _error
->PendingError(); 
 467    if (_config
->FindI("quiet",0) > 0) 
 468       _error
->DumpErrors(); 
 470       _error
->DumpErrors(GlobalError::DEBUG
); 
 471    return Errors 
== true ? 100 : 0;