1 #include "config.h"
2 #include "meta.h"
3 #include "acfg.h"
4
5 #include <acbuf.h>
6 #include <aclogger.h>
7 #include <dirwalk.h>
8 #include <fcntl.h>
9
10 #ifdef HAVE_SSL
11 #include <openssl/evp.h>
12 #endif
13
14 #include <stddef.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <unistd.h>
18 #include <sys/time.h>
19 #include <signal.h>
20 #include <regex.h>
21 #include <errno.h>
22
23 #include <cstdbool>
24 #include <cstdint>
25 #include <cstdlib>
26 #include <ctime>
27 #include <cstdio>
28 #include <cstring>
29 #include <functional>
30
31 #include <iostream>
32 #include <fstream>
33 #include <string>
34 #include <list>
35 #include <queue>
36
37 #include "debug.h"
38 #include "dlcon.h"
39 #include "fileio.h"
40 #include "fileitem.h"
41
42 #ifdef HAVE_SSL
43 #include "openssl/bio.h"
44 #include "openssl/ssl.h"
45 #include "openssl/err.h"
46 #include <openssl/rand.h>
47 #include <openssl/sha.h>
48 #include <openssl/crypto.h>
49 #endif
50
51 #include "filereader.h"
52 #include "csmapping.h"
53 #include "cleaner.h"
54
55 using namespace std;
56 using namespace acng;
57
58 bool g_bVerbose = false;
59
60 // dummies to satisfy references
61 namespace acng
62 {
63 LPCSTR ReTest(LPCSTR);
cleaner()64 cleaner::cleaner() : m_thr(pthread_t()) {}
~cleaner()65 cleaner::~cleaner() {}
ScheduleFor(time_t,cleaner::eType)66 void cleaner::ScheduleFor(time_t, cleaner::eType) {}
67 cleaner g_victor;
68 }
69
70 struct IFitemFactory
71 {
72 virtual SHARED_PTR<fileitem> Create() =0;
73 virtual ~IFitemFactory() =default;
74 };
75
76 struct CPrintItemFactory : public IFitemFactory
77 {
CreateCPrintItemFactory78 virtual SHARED_PTR<fileitem> Create()
79 {
80 class tPrintItem : public fileitem
81 {
82 public:
83 tPrintItem()
84 {
85 m_bAllowStoreData=false;
86 m_nSizeChecked = m_nSizeSeen = 0;
87 };
88 virtual FiStatus Setup(bool) override
89 {
90 m_nSizeChecked = m_nSizeSeen = 0;
91 return m_status = FIST_INITED;
92 }
93 virtual int GetFileFd() override
94 { return 1;}; // something, don't care for now
95 virtual bool DownloadStartedStoreHeader(const header &h, size_t, const char *,
96 bool, bool&) override
97 {
98 m_head = h;
99 auto opt_dbg=getenv("ACNGTOOL_DEBUG_DOWNLOAD");
100 if(opt_dbg && *opt_dbg)
101 std::cerr << (std::string) h.ToString() << std::endl;
102 return true;
103 }
104 virtual bool StoreFileData(const char *data, unsigned int size) override
105 {
106 if(!size)
107 m_status = FIST_COMPLETE;
108
109 return (size==fwrite(data, sizeof(char), size, stdout));
110 }
111 ssize_t SendData(int , int, off_t &, size_t ) override
112 {
113 return 0;
114 }
115 };
116 return make_shared<tPrintItem>();
117 }
118 };
119
120 struct verbprint
121 {
122 int cnt = 0;
dotverbprint123 void dot()
124 {
125 if (!g_bVerbose)
126 return;
127 cnt++;
128 cerr << '.';
129 }
msgverbprint130 void msg(cmstring& msg)
131 {
132 if (!g_bVerbose)
133 return;
134 fin();
135 cerr << msg << endl;
136
137 }
finverbprint138 void fin()
139 {
140 if (!g_bVerbose)
141 return;
142 if (cnt)
143 cerr << endl;
144 cnt = 0;
145 }
146 } vprint;
147
148 struct CReportItemFactory : public IFitemFactory
149 {
CreateCReportItemFactory150 virtual SHARED_PTR<fileitem> Create()
151 {
152 class tRepItem : public fileitem
153 {
154 acbuf lineBuf;
155 string m_key = maark;
156 tStrVec m_errMsg;
157
158 public:
159
160 tRepItem()
161 {
162 m_bAllowStoreData=false;
163 m_nSizeChecked = m_nSizeSeen = 0;
164 lineBuf.setsize(1<<16);
165 memset(lineBuf.wptr(), 0, 1<<16);
166 };
167 virtual FiStatus Setup(bool) override
168 {
169 m_nSizeChecked = m_nSizeSeen = 0;
170 return m_status = FIST_INITED;
171 }
172 virtual int GetFileFd() override
173 { return 1;}; // something, don't care for now
174 virtual bool DownloadStartedStoreHeader(const header &h, size_t, const char *,
175 bool, bool&) override
176 {
177 m_head = h;
178 return true;
179 }
180 virtual bool StoreFileData(const char *data, unsigned int size) override
181 {
182 if(!size)
183 {
184 m_status = FIST_COMPLETE;
185 vprint.fin();
186 }
187 auto consumed = std::min(size, lineBuf.freecapa());
188 memcpy(lineBuf.wptr(), data, consumed);
189 lineBuf.got(consumed);
190 for(;;)
191 {
192 LPCSTR p = lineBuf.rptr();
193 auto end = mempbrk(p, "\r\n", lineBuf.size());
194 if(!end)
195 break;
196 string s(p, end-p);
197 lineBuf.drop(s.length()+1);
198 vprint.dot();
199 if(startsWith(s, m_key))
200 {
201 // that's for us... "<key><type> content\n"
202 char *endchar = nullptr;
203 p = s.c_str();
204 auto val = strtoul(p + m_key.length(), &endchar, 10);
205 if(!endchar || !*endchar)
206 continue; // heh? shall not finish here
207 switch(ControLineType(val))
208 {
209 case ControLineType::BeforeError:
210 m_errMsg.emplace_back(endchar, s.size() - (endchar - p));
211 vprint.msg(m_errMsg.back());
212 break;
213 case ControLineType::Error:
214 {
215 if(!g_bVerbose) // printed before
216 for(auto l : m_errMsg)
217 cerr << l << endl;
218 m_errMsg.clear();
219 string msg(endchar, s.size() - (endchar - p));
220 vprint.fin();
221 cerr << msg << endl;
222 break;
223 }
224 default:
225 continue;
226 }
227 }
228 }
229 return true;
230 }
231 ssize_t SendData(int , int, off_t &, size_t ) override
232 {
233 return 0;
234 }
235 };
236 return make_shared<tRepItem>();
237 }
238 };
239
240 int wcat(LPCSTR url, LPCSTR proxy, IFitemFactory*, IDlConFactory *pdlconfa = &g_tcp_con_factory);
241
usage(int retCode=0,LPCSTR cmd=nullptr)242 static void usage(int retCode = 0, LPCSTR cmd = nullptr)
243 {
244 if(cmd)
245 {
246 if(0 == strcmp(cmd, "shrink"))
247 cerr << "USAGE: acngtool shrink numberX [-f | -n] [-x] [-v] [variable assignments...]" <<endl <<
248 "-f: delete files"<< endl <<
249 "-n: dry run, display results" << endl <<
250 "-v: more verbosity" << endl <<
251 "-x: also drop index files (can be dangerous)" <<endl <<
252 "Suffix X can be k,K,m,M,g,G (for kb,KiB,mb,MiB,gb,GiB)" << endl;
253 }
254 else
255 (retCode ? cout : cerr) <<
256 "Usage: acngtool command parameter... [options]\n\n"
257 "command := { printvar, cfgdump, retest, patch, curl, encb64, maint, shrink }\n"
258 "parameter := (specific to command)\n"
259 "options := (see apt-cacher-ng options)\n"
260 "extra options := -h, --verbose\n"
261 #if SUPPWHASH
262 #warning FIXME
263 "-H: read a password from STDIN and print its hash\n"
264 #endif
265 "\n";
266 exit(retCode);
267 }
268
269 struct pkgEntry
270 {
271 std::string path;
272 time_t lastDate;
273 blkcnt_t blocks;
274 // for prio.queue, oldest shall be on top
operator <pkgEntry275 bool operator<(const pkgEntry &other) const
276 {
277 return lastDate > other.lastDate;
278 }
279 };
280
shrink(off_t wantedSize,bool dryrun,bool apply,bool verbose,bool incIfiles)281 int shrink(off_t wantedSize, bool dryrun, bool apply, bool verbose, bool incIfiles)
282 {
283 if(!dryrun && !apply)
284 {
285 cerr << "Error: needs -f or -n options" << endl;
286 return 97;
287 }
288 if(dryrun && apply)
289 {
290 cerr << "Error: -f and -n are mutually exclusive" <<endl;
291 return 107;
292 }
293 // cout << "wanted: " << wantedSize << endl;
294 std::priority_queue<pkgEntry/*, vector<pkgEntry>, cmpLessDate */ > delQ;
295 std::unordered_map<string, pair<time_t,off_t> > related;
296
297 blkcnt_t totalBlocks = 0;
298
299 IFileHandler::FindFiles(cfg::cachedir,
300 [&delQ, &totalBlocks, &related, &incIfiles](cmstring & path, const struct stat& finfo) -> bool
301 {
302 // reference date used in the prioqueue heap
303 auto dateLatest = max(finfo.st_ctim.tv_sec, finfo.st_mtim.tv_sec);
304 auto isHead = endsWithSzAr(path, ".head");
305 string pkgPath, otherName;
306 if(isHead)
307 {
308 pkgPath = path.substr(0, path.length()-5);
309 otherName = pkgPath;
310 }
311 else
312 {
313 pkgPath = path;
314 otherName = path + ".head";
315 }
316 auto ftype = rex::GetFiletype(pkgPath);
317 if((ftype==rex::FILE_SPECIAL_VOLATILE || ftype == rex::FILE_VOLATILE) && !incIfiles)
318 return true;
319 // anything else is considered junk
320
321 auto other = related.find(otherName);
322 if(other == related.end())
323 {
324 // the related file will appear soon
325 related.insert(make_pair(path, make_pair(dateLatest, finfo.st_blocks)));
326 return true;
327 }
328 // care only about stamps on .head files (track mode)
329 // or ONLY about data file's timestamp (not-track mode)
330 if( (cfg::trackfileuse && !isHead) || (!cfg::trackfileuse && isHead))
331 dateLatest = other->second.first;
332
333 auto bothBlocks = (finfo.st_blocks + other->second.second);
334 related.erase(other);
335
336 totalBlocks += bothBlocks;
337 delQ.push({pkgPath, dateLatest, bothBlocks});
338
339 return true;
340 }
341 , true, false);
342
343 // there might be some unmatched remains...
344 for(auto kv: related)
345 delQ.push({kv.first, kv.second.first, kv.second.second});
346 related.clear();
347
348 auto foundSizeString = offttosHdotted(totalBlocks*512);
349 blkcnt_t wantedBlocks = wantedSize / 512;
350
351 if(totalBlocks < wantedBlocks)
352 {
353 if(verbose)
354 cout << "Requested size smaller than current size, nothing to do." << endl;
355 return 0;
356 }
357
358 if(verbose)
359 {
360 cout << "Found " << foundSizeString << " bytes of relevant data, reducing to "
361 << offttosHdotted(wantedSize) << " (~"<< (wantedBlocks*100/totalBlocks) << "%)"
362 << endl;
363 }
364 while(!delQ.empty())
365 {
366 bool todel = (totalBlocks > wantedBlocks);
367 if(todel)
368 totalBlocks -= delQ.top().blocks;
369 const char *msg = 0;
370 if(verbose || dryrun)
371 msg = (todel ? "Delete: " : "Keep: " );
372 auto& delpath(delQ.top().path);
373 if(msg)
374 cout << msg << delpath << endl << msg << delpath << ".head" << endl;
375 if(todel && apply)
376 {
377 unlink(delpath.c_str());
378 unlink(mstring(delpath + ".head").c_str());
379 }
380 delQ.pop();
381 }
382 if(verbose)
383 {
384 cout << "New size: " << offttosHdotted(totalBlocks*512) << " (before: "
385 << foundSizeString << ")" << endl;
386 }
387 return 0;
388 }
389
390 #if SUPPWHASH
391
hashpwd()392 int hashpwd()
393 {
394 #ifdef HAVE_SSL
395 string plain;
396 uint32_t salt=0;
397 for(unsigned i=10; i; --i)
398 {
399 if(RAND_bytes(reinterpret_cast<unsigned char*>(&salt), 4) >0)
400 break;
401 else
402 salt=0;
403 sleep(1);
404 }
405 if(!salt) // ok, whatever...
406 {
407 uintptr_t pval = reinterpret_cast<uintptr_t>(&plain);
408 srandom(uint(time(0)) + uint(pval) +uint(getpid()));
409 salt=random();
410 timespec ts;
411 clock_gettime(CLOCK_BOOTTIME, &ts);
412 for(auto c=(ts.tv_nsec+ts.tv_sec)%1024 ; c; c--)
413 salt=random();
414 }
415 string crypass = BytesToHexString(reinterpret_cast<const uint8_t*>(&salt), 4);
416 #ifdef DEBUG
417 plain="moopa";
418 #else
419 cin >> plain;
420 #endif
421 trimString(plain);
422 if(!AppendPasswordHash(crypass, plain.data(), plain.size()))
423 return EXIT_FAILURE;
424 cout << crypass <<endl;
425 return EXIT_SUCCESS;
426 #else
427 cerr << "OpenSSL not available, hashing functionality disabled." <<endl;
428 return EXIT_FAILURE;
429 #endif
430 }
431
432
AppendPasswordHash(string & stringWithSalt,LPCSTR plainPass,size_t passLen)433 bool AppendPasswordHash(string &stringWithSalt, LPCSTR plainPass, size_t passLen)
434 {
435 if(stringWithSalt.length()<8)
436 return false;
437
438 uint8_t sum[20];
439 if(1!=PKCS5_PBKDF2_HMAC_SHA1(plainPass, passLen,
440 (unsigned char*) (stringWithSalt.data()+stringWithSalt.size()-8), 8,
441 NUM_PBKDF2_ITERATIONS,
442 sizeof(sum), (unsigned char*) sum))
443 return false;
444 stringWithSalt+=EncodeBase64((LPCSTR)sum, 20);
445 stringWithSalt+="00";
446 #warning dbg
447 // checksum byte
448 uint8_t pCs=0;
449 for(char c : stringWithSalt)
450 pCs+=c;
451 stringWithSalt+=BytesToHexString(&pCs, 1);
452 return true;
453 }
454 #endif
455
456 typedef deque<tPtrLen> tPatchSequence;
457
458 // might need to access the last line externally
459 unsigned long rangeStart(0), rangeLast(0);
460
patchChunk(tPatchSequence & idx,LPCSTR pline,size_t len,tPatchSequence chunk)461 inline bool patchChunk(tPatchSequence& idx, LPCSTR pline, size_t len, tPatchSequence chunk)
462 {
463 char op = 0x0;
464 auto n = sscanf(pline, "%lu,%lu%c\n", &rangeStart, &rangeLast, &op);
465 if (n == 1) // good enough
466 rangeLast = rangeStart, op = pline[len - 2];
467 else if(n!=3)
468 return false; // bad instruction
469 if (rangeStart > idx.size() || rangeLast > idx.size() || rangeStart > rangeLast)
470 return false;
471 if (op == 'a')
472 idx.insert(idx.begin() + (size_t) rangeStart + 1, chunk.begin(), chunk.end());
473 else
474 {
475 size_t i = 0;
476 for (; i < chunk.size(); ++i, ++rangeStart)
477 {
478 if (rangeStart <= rangeLast)
479 idx[rangeStart] = chunk[i];
480 else
481 break; // new stuff bigger than replaced range
482 }
483 if (i < chunk.size()) // not enough space :-(
484 idx.insert(idx.begin() + (size_t) rangeStart, chunk.begin() + i, chunk.end());
485 else if (rangeStart - 1 != rangeLast) // less data now?
486 idx.erase(idx.begin() + (size_t) rangeStart, idx.begin() + (size_t) rangeLast + 1);
487 }
488 return true;
489 }
490
maint_job()491 int maint_job()
492 {
493 cfg::SetOption("proxy=", nullptr);
494
495 tStrVec hostips;
496 #if 0 // FIXME, processing on UDS gets stuck somewhere
497 // prefer UDS if configured in a sane way
498 if (startsWithSz(cfg::fifopath, "/"))
499 hostips.emplace_back(cfg::fifopath);
500 #endif
501 auto nips = Tokenize(cfg::bindaddr, SPACECHARS, hostips, true);
502 if (!nips)
503 hostips.emplace_back("localhost");
504
505 for (const auto& hostaddr : hostips)
506 {
507 // use an own connection factory which does "the right thing" and leaks the
508 // internal connection result
509 struct maintfac: public IDlConFactory
510 {
511 bool m_bOK = false;
512 mstring m_hname;
513 maintfac(cmstring& s) :
514 m_hname(s)
515 {
516 }
517
518 void RecycleIdleConnection(tDlStreamHandle & handle) override
519 {
520 // keep going, no recycling/restoring
521 }
522 virtual tDlStreamHandle CreateConnected(cmstring &, cmstring &, mstring &, bool *,
523 cfg::tRepoData::IHookHandler *, bool, int, bool) override
524 {
525 string serr;
526
527 if (m_hname[0] != '/') // not UDS
528 {
529 auto mhandle = g_tcp_con_factory.CreateConnected(m_hname, cfg::port, serr, 0, 0,
530 false, 30, true);
531 m_bOK = mhandle.get();
532 return mhandle;
533 }
534 // otherwise build a fake connection on unix domain socket
535 struct udsconnection: public tcpconnect
536 {
537 udsconnection(cmstring& udspath, bool *ok) :
538 tcpconnect(nullptr)
539 {
540 #ifdef DEBUG
541 cerr << "Socket path: " << udspath << endl;
542 #endif
543 auto m_conFd = socket(PF_UNIX, SOCK_STREAM, 0);
544 if (m_conFd < 0)
545 return;
546
547 struct sockaddr_un addr;
548 addr.sun_family = PF_UNIX;
549 strcpy(addr.sun_path, cfg::fifopath.c_str());
550 socklen_t adlen =
551 cfg::fifopath.length() + 1 + offsetof(struct sockaddr_un, sun_path);
552 if (connect(m_conFd, (struct sockaddr*) &addr, adlen))
553 {
554 #ifdef DEBUG
555 perror("connect");
556 #endif
557 return;
558 }
559 // basic identification needed
560 tSS ids;
561 ids << "GET / HTTP/1.0\r\nX-Original-Source: localhost\r\n\r\n";
562 if (!ids.send(m_conFd))
563 return;
564
565 #ifdef HAVE_SSL
566 m_ssl = nullptr;
567 m_bio = nullptr;
568 #endif
569 // better match the TCP socket parameters
570 m_sHostName = "localhost";
571 m_sPort = sDefPortHTTP;
572 *ok = true;
573 }
574 };
575 return make_shared<udsconnection>(m_hname, &m_bOK);
576 }
577 };
578 maintfac factoryWrapper(hostaddr);
579 tSS urlPath;
580 urlPath << "http://";
581 if (!cfg::adminauth.empty())
582 urlPath << UserinfoEscape(cfg::adminauth) << "@";
583 if (hostaddr[0] == '/')
584 urlPath << "localhost";
585 else
586 urlPath << hostaddr << ":" << cfg::port;
587
588 if (cfg::reportpage.empty())
589 return -1;
590 if(cfg::reportpage[0] != '/')
591 urlPath << "/";
592 urlPath << cfg::reportpage;
593 LPCSTR req = getenv("ACNGREQ");
594 urlPath << (req ? req : "?doExpire=Start+Expiration&abortOnErrors=aOe");
595
596 #ifdef DEBUG
597 cerr << "Constructed URL: " << (string) urlPath << endl;
598 #endif
599
600 CReportItemFactory printItemFactory;
601 auto retcode = wcat(urlPath.c_str(), nullptr, &printItemFactory, &factoryWrapper);
602 if (retcode)
603 {
604 if (!factoryWrapper.m_bOK) // connection failed, try another IP
605 continue;
606 // otherwise the stuff has been printed
607 return 2;
608 }
609 else
610 return 0;
611 }
612 // all attempts failed
613 return 3;
614 }
615
patch_file(string sBase,string sPatch,string sResult)616 int patch_file(string sBase, string sPatch, string sResult)
617 {
618 filereader frBase, frPatch;
619 if(!frBase.OpenFile(sBase, true) || !frPatch.OpenFile(sPatch, true))
620 return -2;
621 auto buf = frBase.GetBuffer();
622 auto size = frBase.GetSize();
623 tPatchSequence idx;
624 idx.emplace_back(buf, 0); // dummy entry to avoid -1 calculations because of ed numbering style
625 for (auto p = buf; p < buf + size;)
626 {
627 LPCSTR crNext = strchr(p, '\n');
628 if (crNext)
629 {
630 idx.emplace_back(p, crNext + 1 - p);
631 p = crNext + 1;
632 }
633 else
634 {
635 idx.emplace_back(p, buf + size - p);
636 break;
637 }
638 }
639
640 auto pbuf = frPatch.GetBuffer();
641 auto psize = frPatch.GetSize();
642 tPatchSequence chunk;
643 LPCSTR cmd =0;
644 size_t cmdlen = 0;
645 for (auto p = pbuf; p < pbuf + psize;)
646 {
647 LPCSTR crNext = strchr(p, '\n');
648 size_t len = 0;
649 LPCSTR line=p;
650 if (crNext)
651 {
652 len = crNext + 1 - p;
653 p = crNext + 1;
654 }
655 else
656 {
657 len = pbuf + psize - p;
658 p = pbuf + psize + 1; // break signal, actually
659 }
660 p=crNext+1;
661
662 bool gogo = (len == 2 && *line == '.');
663 if(!gogo)
664 {
665 if(!cmdlen)
666 {
667 if(!strncmp("s/.//\n", line, 6))
668 {
669 // oh, that's the fix-the-last-line command :-(
670 if(rangeStart)
671 idx[rangeStart].first = ".\n", idx[rangeStart].second=2;
672 continue;
673 }
674 else if(line[0] == 'w')
675 continue; // don't care, we know the target
676
677 cmdlen = len;
678 cmd = line;
679
680 if(len>2 && line[len-2] == 'd')
681 gogo = true; // no terminator to expect
682 }
683 else
684 chunk.emplace_back(line, len);
685 }
686
687 if(gogo)
688 {
689 if(!patchChunk(idx, cmd, cmdlen, chunk))
690 {
691 cerr << "Bad patch line: ";
692 cerr.write(cmd, cmdlen);
693 exit(EINVAL);
694 }
695 chunk.clear();
696 cmdlen = 0;
697 }
698 }
699 ofstream res(sResult.c_str());
700 if(!res.is_open())
701 return -3;
702
703 for(const auto& kv : idx)
704 res.write(kv.first, kv.second);
705 res.flush();
706 // dump_proc_status_always();
707 return res.good() ? 0 : -4;
708 }
709
710
711 struct parm {
712 unsigned minArg, maxArg; // if maxArg is UINT_MAX, there will be a final call with NULL argument
713 std::function<void(LPCSTR)> f;
714 };
715
716 // some globals shared across the functions
717 int g_exitCode(0);
718 LPCSTR g_missingCfgDir = nullptr;
719
parse_options(int argc,const char ** argv,function<void (LPCSTR)> f)720 void parse_options(int argc, const char **argv, function<void (LPCSTR)> f)
721 {
722 LPCSTR szCfgDir=CFGDIR;
723 std::vector<LPCSTR> validargs, nonoptions;
724 bool ignoreCfgErrors = false;
725
726 for (auto p=argv; p<argv+argc; p++)
727 {
728 if (!strncmp(*p, "-h", 2))
729 usage();
730 else if (!strcmp(*p, "-c"))
731 {
732 ++p;
733 if (p < argv + argc)
734 szCfgDir = *p;
735 else
736 usage(2);
737 }
738 else if(!strcmp(*p, "--verbose"))
739 g_bVerbose=true;
740 else if(!strcmp(*p, "-i"))
741 ignoreCfgErrors = true;
742 else if(**p) // not empty
743 validargs.emplace_back(*p);
744
745 #if SUPPWHASH
746 #warning FIXME
747 else if (!strncmp(*p, "-H", 2))
748 exit(hashpwd());
749 #endif
750 }
751
752 if(szCfgDir)
753 {
754 Cstat info(szCfgDir);
755 if(!info || !S_ISDIR(info.st_mode))
756 g_missingCfgDir = szCfgDir;
757 else
758 cfg::ReadConfigDirectory(szCfgDir, ignoreCfgErrors);
759 }
760
761 tStrVec non_opt_args;
762
763 for(auto& keyval : validargs)
764 {
765 cfg::g_bQuiet = true;
766 if(!cfg::SetOption(keyval, 0))
767 nonoptions.emplace_back(keyval);
768 cfg::g_bQuiet = false;
769 }
770
771 cfg::PostProcConfig();
772
773 for(const auto& x: nonoptions)
774 f(x);
775 }
776
777
778 #if SUPPWHASH
ssl_init()779 void ssl_init()
780 {
781 #ifdef HAVE_SSL
782 SSL_load_error_strings();
783 ERR_load_BIO_strings();
784 ERR_load_crypto_strings();
785 ERR_load_SSL_strings();
786 OpenSSL_add_all_algorithms();
787 SSL_library_init();
788 #endif
789 }
790 #endif
791
792 /*
793 void assert_cfgdir()
794 {
795 if(!g_missingCfgDir)
796 return;
797 cerr << "Failed to open config directory: " << g_missingCfgDir <<endl;
798 exit(EXIT_FAILURE);
799 }
800 */
801
warn_cfgdir()802 void warn_cfgdir()
803 {
804 if (g_missingCfgDir)
805 cerr << "Warning: failed to open config directory: " << g_missingCfgDir <<endl;
806 }
807
808 std::unordered_map<string, parm> parms = {
809 #if 0
810 {
811 "urltest",
812 { 1, 1, [](LPCSTR p)
813 {
814 std::cout << EncodeBase64Auth(p);
815 }
816 }
817 }
818 ,
819 #endif
820 #if 0
821 {
822 "bin2hex",
823 { 1, 1, [](LPCSTR p)
824 {
825 filereader f;
826 if(f.OpenFile(p, true))
827 exit(EIO);
828 std::cout << BytesToHexString(f.GetBuffer(), f.GetSize()) << std::endl;
829 exit(EXIT_SUCCESS);
830 }
831 }
832 }
833 ,
834 #endif
835 #if 0 // def HAVE_DECB64
836 {
837 "decb64",
838 { 1, 1, [](LPCSTR p)
839 {
840 #ifdef DEBUG
841 cerr << "decoding " << p <<endl;
842 #endif
843 acbuf res;
844 if(DecodeBase64(p, strlen(p), res))
845 {
846 std::cout.write(res.rptr(), res.size());
847 exit(0);
848 }
849 exit(1);
850 }
851 }
852 }
853 ,
854 #endif
855 {
856 "encb64",
857 { 1, 1, [](LPCSTR p)
__anon3bf6efdb0202() 858 {
859 #ifdef DEBUG
860 cerr << "encoding " << p <<endl;
861 #endif
862 std::cout << EncodeBase64Auth(p);
863 }
864 }
865 }
866 ,
867 {
868 "cfgdump",
__anon3bf6efdb0302() 869 { 0, 0, [](LPCSTR p) {
870 warn_cfgdir();
871 cfg::dump_config(false);
872 }
873 }
874 }
875 ,
876 {
877 "curl",
878 { 1, UINT_MAX, [](LPCSTR p)
__anon3bf6efdb0402() 879 {
880 if(!p)
881 return;
882
883 CPrintItemFactory fac;
884 auto ret=wcat(p, getenv("http_proxy"), &fac);
885 if(!g_exitCode)
886 g_exitCode = ret;
887
888 }
889 }
890 },
891 {
892 "retest",
893 {
894 1, 1, [](LPCSTR p)
__anon3bf6efdb0502() 895 {
896 warn_cfgdir();
897 std::cout << ReTest(p) << std::endl;
898 }
899 }
900 }
901 ,
902 {
903 "printvar",
904 {
905 1, 1, [](LPCSTR p)
__anon3bf6efdb0602() 906 {
907 warn_cfgdir();
908 auto ps(cfg::GetStringPtr(p));
909 if(ps) { cout << *ps << endl; return; }
910 auto pi(cfg::GetIntPtr(p));
911 if(pi) {
912 cout << *pi << endl;
913 return;
914 }
915 g_exitCode=23;
916 }
917 }
918 },
919 {
920 "patch",
921 {
922 3, 3, [](LPCSTR p)
__anon3bf6efdb0702() 923 {
924 static tStrVec iop;
925 iop.emplace_back(p);
926 if(iop.size() == 3)
927 g_exitCode+=patch_file(iop[0], iop[1], iop[2]);
928 }
929 }
930 }
931
932 ,
933 {
934 "maint",
935 {
936 0, 0, [](LPCSTR p)
__anon3bf6efdb0802() 937 {
938 warn_cfgdir();
939 g_exitCode+=maint_job();
940 }
941 }
942 }
943 ,
944 {
945 "shrink",
946 {
947 1, UINT_MAX, [](LPCSTR p)
__anon3bf6efdb0902() 948 {
949 static bool dryrun(false), apply(false), verbose(false), incIfiles(false);
950 static off_t wantedSize(4000000000);
951 if(!p)
952 g_exitCode += shrink(wantedSize, dryrun, apply, verbose, incIfiles);
953 else if(*p > '0' && *p<='9')
954 wantedSize = strsizeToOfft(p);
955 else if(*p == '-')
956 {
957 for(++p;*p;++p)
958 {
959 if(*p == 'f') apply = true;
960 else if(*p == 'n') dryrun = true;
961 else if (*p == 'x') incIfiles = true;
962 else if (*p == 'v') verbose = true;
963 }
964 }
965 }
966 }
967 }
968 };
969
main(int argc,const char ** argv)970 int main(int argc, const char **argv)
971 {
972 using namespace acng;
973
974 string exe(argv[0]);
975 unsigned aOffset=1;
976 if(endsWithSzAr(exe, "expire-caller.pl"))
977 {
978 aOffset=0;
979 argv[0] = "maint";
980 }
981 cfg::g_bQuiet = false;
982 cfg::g_bNoComplex = true; // no DB for just single variables
983
984 parm* parm = nullptr;
985 LPCSTR mode = nullptr;
986 unsigned xargCount = 0;
987
988 parse_options(argc-aOffset, argv+aOffset, [&](LPCSTR p)
989 {
990 bool bFirst = false;
991 if(!mode)
992 bFirst = (0 != (mode = p));
993 else
994 xargCount++;
995 if(!parm)
996 {
997 auto it = parms.find(mode);
998 if(it == parms.end())
999 usage(1);
1000 parm = & it->second;
1001 }
1002 if(xargCount > parm->maxArg)
1003 usage(2);
1004 if(!bFirst)
1005 parm->f(p);
1006 });
1007 if(!mode || !parm)
1008 usage(3);
1009 #ifdef DEBUG
1010 log::open();
1011 #endif
1012 if(!xargCount) // should run the code at least once?
1013 {
1014 if(parm->minArg) // uh... needs argument(s)
1015 usage(4, mode);
1016 parm->f(nullptr);
1017 }
1018 else if(parm->maxArg == UINT_MAX) // or needs to terminate it?
1019 parm->f(nullptr);
1020 return g_exitCode;
1021 }
1022
wcat(LPCSTR surl,LPCSTR proxy,IFitemFactory * fac,IDlConFactory * pDlconFac)1023 int wcat(LPCSTR surl, LPCSTR proxy, IFitemFactory* fac, IDlConFactory *pDlconFac)
1024 {
1025 cfg::dnscachetime=0;
1026 cfg::persistoutgoing=0;
1027 cfg::badredmime.clear();
1028 cfg::redirmax=10;
1029
1030 if(proxy)
1031 if(cfg::SetOption(string("proxy:")+proxy, nullptr))
1032 return -1;
1033 tHttpUrl url;
1034 if(!surl)
1035 return 2;
1036 string xurl(surl);
1037 if(!url.SetHttpUrl(xurl, false))
1038 return -2;
1039 dlcon dl(true, nullptr, pDlconFac);
1040
1041 auto fi=fac->Create();
1042 dl.AddJob(fi, &url, nullptr, nullptr, 0, cfg::REDIRMAX_DEFAULT);
1043 dl.WorkLoop();
1044 auto fistatus = fi->GetStatus();
1045 header hh = fi->GetHeader();
1046 int st=hh.getStatus();
1047
1048 if(fistatus == fileitem::FIST_COMPLETE && st == 200)
1049 return EXIT_SUCCESS;
1050
1051 // don't reveal passwords
1052 auto xpos=xurl.find('@');
1053 if(xpos!=stmiss)
1054 xurl.erase(0, xpos+1);
1055 cerr << "Error: cannot fetch " << xurl <<", " << hh.frontLine << endl;
1056 if (st>=500)
1057 return EIO;
1058 if (st>=400)
1059 return EACCES;
1060
1061 return EXIT_FAILURE;
1062 }
1063
1064 #if 0
1065
1066 void do_stuff_before_config()
1067 {
1068 LPCSTR envvar(nullptr);
1069
1070 cerr << "Pandora: " << sizeof(regex_t) << endl;
1071 /*
1072 // PLAYGROUND
1073 if (argc < 2)
1074 return -1;
1075
1076 acng::cfg:tHostInfo hi;
1077 cout << "Parsing " << argv[1] << ", result: " << hi.SetUrl(argv[1]) << endl;
1078 cout << "Host: " << hi.sHost << ", Port: " << hi.sPort << ", Path: "
1079 << hi.sPath << endl;
1080 return 0;
1081
1082 bool Bz2compressFile(const char *, const char*);
1083 return !Bz2compressFile(argv[1], argv[2]);
1084
1085 char tbuf[40];
1086 FormatCurrentTime(tbuf);
1087 std::cerr << tbuf << std::endl;
1088 exit(1);
1089 */
1090 envvar = getenv("PARSEIDX");
1091 if (envvar)
1092 {
1093 int parseidx_demo(LPCSTR);
1094 exit(parseidx_demo(envvar));
1095 }
1096
1097 envvar = getenv("GETSUM");
1098 if (envvar)
1099 {
1100 uint8_t csum[20];
1101 string s(envvar);
1102 off_t resSize;
1103 bool ok = filereader::GetChecksum(s, CSTYPE_SHA1, csum, false, resSize /*, stdout*/);
1104 if(!ok)
1105 {
1106 perror("");
1107 exit(1);
1108 }
1109 for (unsigned i = 0; i < sizeof(csum); i++)
1110 printf("%02x", csum[i]);
1111 printf("\n");
1112 envvar = getenv("REFSUM");
1113 if (ok && envvar)
1114 {
1115 if(CsEqual(envvar, csum, sizeof(csum)))
1116 {
1117 printf("IsOK\n");
1118 exit(0);
1119 }
1120 else
1121 {
1122 printf("Diff\n");
1123 exit(1);
1124 }
1125 }
1126 exit(0);
1127 }
1128 }
1129
1130 #endif
1131 #if 0
1132 #warning line reader test enabled
1133 if (cmd == "wcl")
1134 {
1135 if (argc < 3)
1136 usage(2);
1137 filereader r;
1138 if (!r.OpenFile(argv[2], true))
1139 {
1140 cerr << r.getSErrorString() << endl;
1141 return EXIT_FAILURE;
1142 }
1143 size_t count = 0;
1144 auto p = r.GetBuffer();
1145 auto e = p + r.GetSize();
1146 for (;p < e; ++p)
1147 count += (*p == '\n');
1148 cout << count << endl;
1149
1150 exit(EXIT_SUCCESS);
1151 }
1152 #endif
1153 #if 0
1154 #warning header parser enabled
1155 if (cmd == "htest")
1156 {
1157 header h;
1158 h.LoadFromFile(argv[2]);
1159 cout << string(h.ToString()) << endl;
1160
1161 h.clear();
1162 filereader r;
1163 r.OpenFile(argv[2]);
1164 std::vector<std::pair<std::string, std::string>> oh;
1165 h.Load(r.GetBuffer(), r.GetSize(), &oh);
1166 for(auto& r : oh)
1167 cout << "X:" << r.first << " to " << r.second;
1168 exit(0);
1169 }
1170 #endif
1171 #if 0
1172 #warning benchmark enabled
1173 if (cmd == "benchmark")
1174 {
1175 dump_proc_status_always();
1176 cfg::g_bQuiet = true;
1177 cfg::g_bNoComplex = false;
1178 parse_options(argc - 2, argv + 2, true);
1179 cfg::PostProcConfig();
1180 string s;
1181 tHttpUrl u;
1182 int res=0;
1183 /*
1184 acng::cfg:tRepoResolvResult hm;
1185 tHttpUrl wtf;
1186 wtf.SetHttpUrl(non_opt_args.front());
1187 acng::cfg:GetRepNameAndPathResidual(wtf, hm);
1188 */
1189 while(cin)
1190 {
1191 std::getline(cin, s);
1192 s += "/xtest.deb";
1193 if(u.SetHttpUrl(s))
1194 {
1195 cfg::tRepoResolvResult xdata;
1196 cfg::GetRepNameAndPathResidual(u, xdata);
1197 cout << s << " -> "
1198 << (xdata.psRepoName ? "matched" : "not matched")
1199 << endl;
1200 }
1201 }
1202 dump_proc_status_always();
1203 exit(res);
1204 }
1205 #endif
1206