4 * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>
5 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
6 * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
7 * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu>
8 * Copyright (c) 2006 by David Kimpe <dnaku@frugalware.org>
9 * Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
38 #include <sys/types.h>
41 #include <locale.h> /* setlocale */
45 #include <archive_entry.h>
48 #include <openssl/md5.h>
58 #include "alpm_list.h"
62 /* This is a replacement for strsep which is not portable (missing on Solaris).
63 * Copyright (c) 2001 by François Gouget <fgouget_at_codeweavers.com> */
64 char* strsep(char** str
, const char* delims
)
75 if (strchr(delims
,**str
)!=NULL
) {
82 /* There is no other token */
88 int _alpm_makepath(const char *path
)
90 return(_alpm_makepath_mode(path
, 0755));
93 /* does the same thing as 'mkdir -p' */
94 int _alpm_makepath_mode(const char *path
, mode_t mode
)
96 /* A bit of pointer hell here. Descriptions:
97 * orig - a copy of path so we can safely butcher it with strsep
98 * str - the current position in the path string (after the delimiter)
99 * ptr - the original position of str after calling strsep
100 * incr - incrementally generated path for use in stat/mkdir call
102 char *orig
, *str
, *ptr
, *incr
;
103 mode_t oldmask
= umask(0000);
107 incr
= calloc(strlen(orig
) + 1, sizeof(char));
109 while((ptr
= strsep(&str
, "/"))) {
111 /* we have another path component- append the newest component to
112 * existing string and create one more level of dir structure */
115 if(access(incr
, F_OK
)) {
116 if(mkdir(incr
, mode
)) {
129 #define CPBUFSIZE 8 * 1024
131 int _alpm_copyfile(const char *src
, const char *dest
)
138 in
= fopen(src
, "rb");
142 out
= fopen(dest
, "wb");
148 CALLOC(buf
, (size_t)CPBUFSIZE
, (size_t)1, ret
= 1; goto cleanup
;);
150 /* do the actual file copy */
151 while((len
= fread(buf
, 1, CPBUFSIZE
, in
))) {
153 nwritten
= fwrite(buf
, 1, len
, out
);
154 if((nwritten
!= len
) || ferror(out
)) {
155 pm_errno
= PM_ERR_WRITE
;
156 _alpm_log(PM_LOG_ERROR
, _("error writing to file '%s': %s\n"),
157 dest
, strerror(errno
));
163 /* chmod dest to permissions of src, as long as it is not a symlink */
165 if(!stat(src
, &statbuf
)) {
166 if(! S_ISLNK(statbuf
.st_mode
)) {
167 fchmod(fileno(out
), statbuf
.st_mode
);
170 /* stat was unsuccessful */
181 /* Trim whitespace and newlines from a string
183 char *_alpm_strtrim(char *str
)
188 /* string is empty, so we're done. */
192 while(isspace((unsigned char)*pch
)) {
196 memmove(str
, pch
, (strlen(pch
) + 1));
199 /* check if there wasn't anything but whitespace in the string. */
204 pch
= (str
+ (strlen(str
) - 1));
205 while(isspace((unsigned char)*pch
)) {
213 /* Create a lock file */
214 int _alpm_lckmk(void)
218 const char *file
= alpm_option_get_lockfile();
220 /* create the dir of the lockfile first */
222 ptr
= strrchr(dir
, '/');
230 fd
= open(file
, O_WRONLY
| O_CREAT
| O_EXCL
, 0000);
231 } while (fd
== -1 && errno
== EINTR
);
233 FILE *f
= fdopen(fd
, "w");
234 fprintf(f
, "%ld\n", (long)getpid());
243 /* Remove a lock file */
244 int _alpm_lckrm(void)
246 const char *file
= alpm_option_get_lockfile();
247 if(unlink(file
) == -1 && errno
!= ENOENT
) {
253 /* Compression functions */
256 * @brief Unpack a specific file in an archive.
258 * @param archive the archive to unpack
259 * @param prefix where to extract the files
260 * @param fn a file within the archive to unpack
261 * @return 0 on success, 1 on failure
263 int _alpm_unpack_single(const char *archive
, const char *prefix
, const char *fn
)
265 alpm_list_t
*list
= NULL
;
270 list
= alpm_list_add(list
, (void *)fn
);
271 ret
= _alpm_unpack(archive
, prefix
, list
, 1);
272 alpm_list_free(list
);
277 * @brief Unpack a list of files in an archive.
279 * @param archive the archive to unpack
280 * @param prefix where to extract the files
281 * @param list a list of files within the archive to unpack or
283 * @param breakfirst break after the first entry found
285 * @return 0 on success, 1 on failure
287 int _alpm_unpack(const char *archive
, const char *prefix
, alpm_list_t
*list
, int breakfirst
)
291 struct archive
*_archive
;
292 struct archive_entry
*entry
;
298 if((_archive
= archive_read_new()) == NULL
)
299 RET_ERR(PM_ERR_LIBARCHIVE
, 1);
301 archive_read_support_compression_all(_archive
);
302 archive_read_support_format_all(_archive
);
304 if(archive_read_open_filename(_archive
, archive
,
305 ARCHIVE_DEFAULT_BYTES_PER_BLOCK
) != ARCHIVE_OK
) {
306 _alpm_log(PM_LOG_ERROR
, _("could not open %s: %s\n"), archive
,
307 archive_error_string(_archive
));
308 RET_ERR(PM_ERR_PKG_OPEN
, 1);
311 oldmask
= umask(0022);
313 /* save the cwd so we can restore it later */
314 if(getcwd(cwd
, PATH_MAX
) == NULL
) {
315 _alpm_log(PM_LOG_ERROR
, _("could not get current working directory\n"));
320 /* just in case our cwd was removed in the upgrade operation */
321 if(chdir(prefix
) != 0) {
322 _alpm_log(PM_LOG_ERROR
, _("could not change directory to %s (%s)\n"), prefix
, strerror(errno
));
327 while(archive_read_next_header(_archive
, &entry
) == ARCHIVE_OK
) {
328 const struct stat
*st
;
329 const char *entryname
; /* the name of the file in the archive */
331 st
= archive_entry_stat(entry
);
332 entryname
= archive_entry_pathname(entry
);
334 if(S_ISREG(st
->st_mode
)) {
335 archive_entry_set_perm(entry
, 0644);
336 } else if(S_ISDIR(st
->st_mode
)) {
337 archive_entry_set_perm(entry
, 0755);
340 /* If specific files were requested, skip entries that don't match. */
342 char *prefix
= strdup(entryname
);
343 char *p
= strstr(prefix
,"/");
347 char *found
= alpm_list_find_str(list
, prefix
);
350 if (archive_read_data_skip(_archive
) != ARCHIVE_OK
) {
356 _alpm_log(PM_LOG_DEBUG
, "extracting: %s\n", entryname
);
360 /* Extract the archive entry. */
361 int readret
= archive_read_extract(_archive
, entry
, 0);
362 if(readret
== ARCHIVE_WARN
) {
363 /* operation succeeded but a non-critical error was encountered */
364 _alpm_log(PM_LOG_WARNING
, _("warning given while extracting %s (%s)\n"),
365 entryname
, archive_error_string(_archive
));
366 } else if(readret
!= ARCHIVE_OK
) {
367 _alpm_log(PM_LOG_ERROR
, _("could not extract %s (%s)\n"),
368 entryname
, archive_error_string(_archive
));
380 archive_read_finish(_archive
);
381 if(restore_cwd
&& chdir(cwd
) != 0) {
382 _alpm_log(PM_LOG_ERROR
, _("could not change directory to %s (%s)\n"), cwd
, strerror(errno
));
387 /* does the same thing as 'rm -rf' */
388 int _alpm_rmrf(const char *path
)
396 if(_alpm_lstat(path
, &st
) == 0) {
397 if(!S_ISDIR(st
.st_mode
)) {
401 if(errno
== ENOENT
) {
408 dirp
= opendir(path
);
412 for(dp
= readdir(dirp
); dp
!= NULL
; dp
= readdir(dirp
)) {
414 sprintf(name
, "%s/%s", path
, dp
->d_name
);
415 if(strcmp(dp
->d_name
, "..") != 0 && strcmp(dp
->d_name
, ".") != 0) {
416 errflag
+= _alpm_rmrf(name
);
430 int _alpm_logaction(int usesyslog
, FILE *f
, const char *fmt
, va_list args
)
435 /* we can't use a va_list more than once, so we need to copy it
436 * so we can use the original when calling vfprintf below. */
438 va_copy(args_syslog
, args
);
439 vsyslog(LOG_WARNING
, fmt
, args_syslog
);
450 /* Use ISO-8601 date format */
451 fprintf(f
, "[%04d-%02d-%02d %02d:%02d] ",
452 tm
->tm_year
+1900, tm
->tm_mon
+1, tm
->tm_mday
,
453 tm
->tm_hour
, tm
->tm_min
);
454 ret
= vfprintf(f
, fmt
, args
);
461 int _alpm_run_chroot(const char *root
, const char *path
, char *const argv
[])
471 /* save the cwd so we can restore it later */
472 if(getcwd(cwd
, PATH_MAX
) == NULL
) {
473 _alpm_log(PM_LOG_ERROR
, _("could not get current working directory\n"));
478 /* just in case our cwd was removed in the upgrade operation */
479 if(chdir(root
) != 0) {
480 _alpm_log(PM_LOG_ERROR
, _("could not change directory to %s (%s)\n"), root
, strerror(errno
));
484 _alpm_log(PM_LOG_DEBUG
, "executing \"%s\" under chroot \"%s\"\n", path
, root
);
486 /* Flush open fds before fork() to avoid cloning buffers */
489 if(pipe(pipefd
) == -1) {
490 _alpm_log(PM_LOG_ERROR
, _("could not create pipe (%s)\n"), strerror(errno
));
495 /* fork- parent and child each have seperate code blocks below */
498 _alpm_log(PM_LOG_ERROR
, _("could not fork a new process (%s)\n"), strerror(errno
));
504 /* this code runs for the child only (the actual chroot/exec) */
507 while(dup2(pipefd
[1], 1) == -1 && errno
== EINTR
);
508 while(dup2(pipefd
[1], 2) == -1 && errno
== EINTR
);
512 /* use fprintf instead of _alpm_log to send output through the parent */
513 if(chroot(root
) != 0) {
514 fprintf(stderr
, _("could not change the root directory (%s)\n"), strerror(errno
));
517 if(chdir("/") != 0) {
518 fprintf(stderr
, _("could not change directory to / (%s)\n"), strerror(errno
));
523 fprintf(stderr
, _("call to execv failed (%s)\n"), strerror(errno
));
526 /* this code runs for the parent only (wait on the child) */
531 pipe
= fdopen(pipefd
[0], "r");
538 if(fgets(line
, PATH_MAX
, pipe
) == NULL
)
540 alpm_logaction("%s", line
);
541 EVENT(handle
->trans
, PM_TRANS_EVT_SCRIPTLET_INFO
, line
, NULL
);
546 while(waitpid(pid
, &status
, 0) == -1) {
548 _alpm_log(PM_LOG_ERROR
, _("call to waitpid failed (%s)\n"), strerror(errno
));
554 /* report error from above after the child has exited */
556 _alpm_log(PM_LOG_ERROR
, _("could not open pipe (%s)\n"), strerror(errno
));
559 /* check the return status, make sure it is 0 (success) */
560 if(WIFEXITED(status
)) {
561 _alpm_log(PM_LOG_DEBUG
, "call to waitpid succeeded\n");
562 if(WEXITSTATUS(status
) != 0) {
563 _alpm_log(PM_LOG_ERROR
, _("command failed to execute correctly\n"));
570 if(restore_cwd
&& chdir(cwd
) != 0) {
571 _alpm_log(PM_LOG_ERROR
, _("could not change directory to %s (%s)\n"), cwd
, strerror(errno
));
577 int _alpm_ldconfig(const char *root
)
581 _alpm_log(PM_LOG_DEBUG
, "running ldconfig\n");
583 snprintf(line
, PATH_MAX
, "%setc/ld.so.conf", root
);
584 if(access(line
, F_OK
) == 0) {
585 snprintf(line
, PATH_MAX
, "%ssbin/ldconfig", root
);
586 if(access(line
, X_OK
) == 0) {
587 char *argv
[] = { "ldconfig", NULL
};
588 _alpm_run_chroot(root
, "/sbin/ldconfig", argv
);
595 /* Helper function for comparing strings using the
596 * alpm "compare func" signature */
597 int _alpm_str_cmp(const void *s1
, const void *s2
)
599 return(strcmp(s1
, s2
));
602 /** Find a filename in a registered alpm cachedir.
603 * @param filename name of file to find
604 * @return malloced path of file, NULL if not found
606 char *_alpm_filecache_find(const char* filename
)
612 /* Loop through the cache dirs until we find a matching file */
613 for(i
= alpm_option_get_cachedirs(); i
; i
= alpm_list_next(i
)) {
614 snprintf(path
, PATH_MAX
, "%s%s", (char*)alpm_list_getdata(i
),
616 if(access(path
, R_OK
) == 0) {
617 retpath
= strdup(path
);
618 _alpm_log(PM_LOG_DEBUG
, "found cached pkg: %s\n", retpath
);
622 /* package wasn't found in any cachedir */
626 /** Check the alpm cachedirs for existance and find a writable one.
627 * If no valid cache directory can be found, use /tmp.
628 * @return pointer to a writable cache directory.
630 const char *_alpm_filecache_setup(void)
633 alpm_list_t
*i
, *tmp
;
636 /* Loop through the cache dirs until we find a writeable dir */
637 for(i
= alpm_option_get_cachedirs(); i
; i
= alpm_list_next(i
)) {
638 cachedir
= alpm_list_getdata(i
);
639 if(stat(cachedir
, &buf
) != 0) {
640 /* cache directory does not exist.... try creating it */
641 _alpm_log(PM_LOG_WARNING
, _("no %s cache exists, creating...\n"),
643 if(_alpm_makepath(cachedir
) == 0) {
644 _alpm_log(PM_LOG_DEBUG
, "using cachedir: %s\n", cachedir
);
647 } else if(S_ISDIR(buf
.st_mode
) && (buf
.st_mode
& S_IWUSR
)) {
648 _alpm_log(PM_LOG_DEBUG
, "using cachedir: %s\n", cachedir
);
653 /* we didn't find a valid cache directory. use /tmp. */
654 tmp
= alpm_list_add(NULL
, strdup("/tmp/"));
655 alpm_option_set_cachedirs(tmp
);
656 _alpm_log(PM_LOG_DEBUG
, "using cachedir: %s", "/tmp/\n");
657 _alpm_log(PM_LOG_WARNING
, _("couldn't create package cache, using /tmp instead\n"));
658 return(alpm_list_getdata(tmp
));
661 /** lstat wrapper that treats /path/dirsymlink/ the same as /path/dirsymlink.
662 * Linux lstat follows POSIX semantics and still performs a dereference on
663 * the first, and for uses of lstat in libalpm this is not what we want.
664 * @param path path to file to lstat
665 * @param buf structure to fill with stat information
666 * @return the return code from lstat
668 int _alpm_lstat(const char *path
, struct stat
*buf
)
671 char *newpath
= strdup(path
);
672 size_t len
= strlen(newpath
);
674 /* strip the trailing slash if one exists */
675 if(len
!= 0 && newpath
[len
- 1] == '/') {
676 newpath
[len
- 1] = '\0';
679 ret
= lstat(newpath
, buf
);
686 static int md5_file(const char *path
, unsigned char output
[16])
693 CALLOC(buf
, 8192, sizeof(unsigned char), return(1));
695 if((f
= fopen(path
, "rb")) == NULL
) {
702 while((n
= fread(buf
, 1, sizeof(buf
), f
)) > 0) {
703 MD5_Update(&ctx
, buf
, n
);
706 MD5_Final(output
, &ctx
);
708 memset(&ctx
, 0, sizeof(MD5_CTX
));
721 /** Get the md5 sum of file.
722 * @param filename name of the file
723 * @return the checksum on success, NULL on error
724 * @addtogroup alpm_misc
726 char SYMEXPORT
*alpm_compute_md5sum(const char *filename
)
728 unsigned char output
[16];
734 ASSERT(filename
!= NULL
, return(NULL
));
736 /* allocate 32 chars plus 1 for null */
737 md5sum
= calloc(33, sizeof(char));
738 /* defined above for OpenSSL, otherwise defined in md5.h */
739 ret
= md5_file(filename
, output
);
742 RET_ERR(PM_ERR_NOT_A_FILE
, NULL
);
745 /* Convert the result to something readable */
746 for (i
= 0; i
< 16; i
++) {
747 /* sprintf is acceptable here because we know our output */
748 sprintf(md5sum
+(i
* 2), "%02x", output
[i
]);
752 _alpm_log(PM_LOG_DEBUG
, "md5(%s) = %s\n", filename
, md5sum
);
756 int _alpm_test_md5sum(const char *filepath
, const char *md5sum
)
761 md5sum2
= alpm_compute_md5sum(filepath
);
763 if(md5sum
== NULL
|| md5sum2
== NULL
) {
765 } else if(strcmp(md5sum
, md5sum2
) != 0) {
775 /* Note: does NOT handle sparse files on purpose for speed. */
776 int _alpm_archive_fgets(struct archive
*a
, struct archive_read_buffer
*b
)
783 /* have we processed this entire block? */
784 if(b
->block
+ b
->block_size
== b
->block_offset
) {
785 if(b
->ret
== ARCHIVE_EOF
) {
786 /* reached end of archive on the last read, now we are out of data */
790 /* zero-copy - this is the entire next block of data. */
791 b
->ret
= archive_read_data_block(a
, (void*)&b
->block
,
792 &b
->block_size
, &offset
);
793 b
->block_offset
= b
->block
;
795 /* error or end of archive with no data read, cleanup */
796 if(b
->ret
< ARCHIVE_OK
||
797 (b
->block_size
== 0 && b
->ret
== ARCHIVE_EOF
)) {
802 /* loop through the block looking for EOL characters */
803 for(i
= b
->block_offset
; i
< (b
->block
+ b
->block_size
); i
++) {
804 /* check if read value was null or newline */
805 if(*i
== '\0' || *i
== '\n') {
811 /* allocate our buffer, or ensure our existing one is big enough */
813 /* set the initial buffer to the read block_size */
814 CALLOC(b
->line
, b
->block_size
+ 1, sizeof(char),
815 RET_ERR(PM_ERR_MEMORY
, -1));
816 b
->line_size
= b
->block_size
+ 1;
817 b
->line_offset
= b
->line
;
819 size_t needed
= (size_t)((b
->line_offset
- b
->line
)
820 + (i
- b
->block_offset
) + 1);
821 if(needed
> b
->max_line_size
) {
822 RET_ERR(PM_ERR_MEMORY
, -1);
824 if(needed
> b
->line_size
) {
825 /* need to realloc + copy data to fit total length */
827 CALLOC(new, needed
, sizeof(char), RET_ERR(PM_ERR_MEMORY
, -1));
828 memcpy(new, b
->line
, b
->line_size
);
829 b
->line_size
= needed
;
830 b
->line_offset
= new + (b
->line_offset
- b
->line
);
837 size_t len
= (size_t)(i
- b
->block_offset
);
838 memcpy(b
->line_offset
, b
->block_offset
, len
);
839 b
->line_offset
[len
] = '\0';
840 b
->block_offset
= ++i
;
841 /* this is the main return point; from here you can read b->line */
844 /* we've looked through the whole block but no newline, copy it */
845 size_t len
= (size_t)(b
->block
+ b
->block_size
- b
->block_offset
);
846 memcpy(b
->line_offset
, b
->block_offset
, len
);
847 b
->line_offset
+= len
;
856 memset(b
, 0, sizeof(b
));
861 int _alpm_splitname(const char *target
, pmpkg_t
*pkg
)
863 /* the format of a db entry is as follows:
864 * package-version-rel/
865 * package name can contain hyphens, so parse from the back- go back
866 * two hyphens and we have split the version from the name.
868 const char *version
, *end
;
870 if(target
== NULL
|| pkg
== NULL
) {
873 end
= target
+ strlen(target
);
875 /* remove any trailing '/' */
876 while (*(end
- 1) == '/') {
880 /* do the magic parsing- find the beginning of the version string
881 * by doing two iterations of same loop to lop off two hyphens */
882 for(version
= end
- 1; *version
&& *version
!= '-'; version
--);
883 for(version
= version
- 1; *version
&& *version
!= '-'; version
--);
884 if(*version
!= '-' || version
== target
) {
888 /* copy into fields and return */
892 /* version actually points to the dash, so need to increment 1 and account
893 * for potential end character */
894 STRNDUP(pkg
->version
, version
+ 1, end
- version
- 1,
895 RET_ERR(PM_ERR_MEMORY
, -1));
900 STRNDUP(pkg
->name
, target
, version
- target
, RET_ERR(PM_ERR_MEMORY
, -1));
901 pkg
->name_hash
= _alpm_hash_sdbm(pkg
->name
);
907 * Hash the given string to an unsigned long value.
908 * This is the standard sdbm hashing algorithm.
909 * @param str string to hash
910 * @return the hash value of the given string
912 unsigned long _alpm_hash_sdbm(const char *str
)
914 unsigned long hash
= 0;
920 while((c
= *str
++)) {
921 hash
= c
+ (hash
<< 6) + (hash
<< 16) - hash
;
927 long _alpm_parsedate(const char *line
)
929 if(isalpha((unsigned char)line
[0])) {
930 /* initialize to null in case of failure */
931 struct tm tmp_tm
= { 0 };
932 setlocale(LC_TIME
, "C");
933 strptime(line
, "%a %b %e %H:%M:%S %Y", &tmp_tm
);
934 setlocale(LC_TIME
, "");
935 return(mktime(&tmp_tm
));
941 /* A quick and dirty implementation derived from glibc */
942 static size_t strnlen(const char *s
, size_t max
)
944 register const char *p
;
945 for(p
= s
; *p
&& max
--; ++p
);
949 char *strndup(const char *s
, size_t n
)
951 size_t len
= strnlen(s
, n
);
952 char *new = (char *) malloc(len
+ 1);
958 return (char *) memcpy(new, s
, len
);
962 /* vim: set ts=2 sw=2 noet: */