| |
| |
| |
| |
| |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdbool.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <sys/stat.h> |
| |
| #define ARGV_DRIVER |
| |
| #include <slibtool/slibtool.h> |
| #include "slibtool_driver_impl.h" |
| #include "slibtool_uninstall_impl.h" |
| #include "slibtool_readlink_impl.h" |
| #include "slibtool_errinfo_impl.h" |
| #include "argv/argv.h" |
| |
| static int slbt_uninstall_usage( |
| int fdout, |
| const char * program, |
| const char * arg, |
| const struct argv_option ** optv, |
| struct argv_meta * meta, |
| int noclr) |
| { |
| char header[512]; |
| |
| snprintf(header,sizeof(header), |
| "Usage: %s --mode=uninstall <rm> [options] [DEST]...\n" |
| "Options:\n", |
| program); |
| |
| switch (noclr) { |
| case 0: |
| argv_usage(fdout,header,optv,arg); |
| break; |
| |
| default: |
| argv_usage_plain(fdout,header,optv,arg); |
| break; |
| } |
| |
| argv_free(meta); |
| |
| return SLBT_USAGE; |
| } |
| |
| static int slbt_exec_uninstall_fail( |
| struct slbt_exec_ctx * actx, |
| struct argv_meta * meta, |
| int ret) |
| { |
| argv_free(meta); |
| slbt_free_exec_ctx(actx); |
| return ret; |
| } |
| |
| static int slbt_exec_uninstall_fs_entry( |
| const struct slbt_driver_ctx * dctx, |
| struct slbt_exec_ctx * ectx, |
| char ** parg, |
| char * path, |
| uint32_t flags) |
| { |
| struct stat st; |
| int fdcwd; |
| char * slash; |
| char dpath[PATH_MAX]; |
| |
| |
| fdcwd = slbt_driver_fdcwd(dctx); |
| |
| |
| if (fstatat(fdcwd,path,&st,0) && (errno == ENOENT)) |
| if (fstatat(fdcwd,path,&st,AT_SYMLINK_NOFOLLOW)) |
| if (errno == ENOENT) |
| return 0; |
| |
| |
| *parg = path; |
| |
| if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT)) |
| if (slbt_output_uninstall(dctx,ectx)) |
| return SLBT_NESTED_ERROR(dctx); |
| |
| |
| if (S_ISDIR(st.st_mode)) { |
| if (!unlinkat(fdcwd,path,AT_REMOVEDIR)) |
| return 0; |
| |
| else if ((errno == EEXIST) || (errno == ENOTEMPTY)) |
| return 0; |
| |
| else |
| return SLBT_SYSTEM_ERROR(dctx,path); |
| } |
| |
| |
| if (unlinkat(fdcwd,path,0)) |
| return SLBT_SYSTEM_ERROR(dctx,path); |
| |
| |
| if (flags & SLBT_UNINSTALL_RMDIR) { |
| strcpy(dpath,path); |
| |
| |
| if (!(slash = strrchr(dpath,'/'))) |
| return 0; |
| |
| *slash = 0; |
| |
| if (unlinkat(fdcwd,dpath,AT_REMOVEDIR)) |
| return SLBT_SYSTEM_ERROR(dctx,dpath); |
| } |
| |
| return 0; |
| } |
| |
| static int slbt_exec_uninstall_versioned_library( |
| const struct slbt_driver_ctx * dctx, |
| struct slbt_exec_ctx * ectx, |
| char ** parg, |
| char * rpath, |
| char * lpath, |
| const char * suffix, |
| uint32_t flags) |
| { |
| char * slash; |
| char * dot; |
| char * path; |
| char apath[PATH_MAX]; |
| |
| |
| if (lpath[0] == '/') { |
| path = lpath; |
| |
| } else if (!(slash = strrchr(rpath,'/'))) { |
| path = lpath; |
| |
| } else { |
| strcpy(apath,rpath); |
| strcpy(&apath[slash-rpath+1],lpath); |
| path = apath; |
| } |
| |
| |
| while ((dot = strrchr(path,'.')) && (strcmp(dot,suffix))) { |
| if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) |
| return SLBT_NESTED_ERROR(dctx); |
| |
| *dot = 0; |
| } |
| |
| return 0; |
| } |
| |
| static int slbt_exec_uninstall_entry( |
| const struct slbt_driver_ctx * dctx, |
| struct slbt_exec_ctx * ectx, |
| struct argv_entry * entry, |
| char ** parg, |
| uint32_t flags) |
| { |
| int fdcwd; |
| const char * dsosuffix; |
| char * dot; |
| char path [PATH_MAX]; |
| char lpath[PATH_MAX]; |
| |
| if ((size_t)snprintf(path,PATH_MAX,"%s", |
| entry->arg) >= PATH_MAX-8) |
| return SLBT_BUFFER_ERROR(dctx); |
| |
| *parg = (char *)entry->arg; |
| |
| |
| fdcwd = slbt_driver_fdcwd(dctx); |
| |
| |
| if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) |
| return SLBT_NESTED_ERROR(dctx); |
| |
| |
| if (!(dot = strrchr(path,'.'))) |
| return 0; |
| |
| else if (strcmp(dot,".la")) |
| return 0; |
| |
| |
| strcpy(dot,".a"); |
| |
| if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) |
| return SLBT_NESTED_ERROR(dctx); |
| |
| |
| dsosuffix = dctx->cctx->settings.dsosuffix; |
| |
| |
| strcpy(dot,dsosuffix); |
| |
| if (!(slbt_readlinkat(fdcwd,path,lpath,sizeof(lpath)))) |
| if (slbt_exec_uninstall_versioned_library( |
| dctx,ectx,parg, |
| path,lpath, |
| dsosuffix,flags)) |
| return SLBT_NESTED_ERROR(dctx); |
| |
| |
| strcpy(dot,".lib.a"); |
| |
| if (!(slbt_readlinkat(fdcwd,path,lpath,sizeof(lpath)))) |
| if (slbt_exec_uninstall_versioned_library( |
| dctx,ectx,parg, |
| path,lpath, |
| ".lib.a",flags)) |
| return SLBT_NESTED_ERROR(dctx); |
| |
| |
| strcpy(dot,".dll"); |
| |
| if (!(slbt_readlinkat(fdcwd,path,lpath,sizeof(lpath)))) |
| if (slbt_exec_uninstall_versioned_library( |
| dctx,ectx,parg, |
| path,lpath, |
| ".dll",flags)) |
| return SLBT_NESTED_ERROR(dctx); |
| |
| |
| strcpy(dot,dsosuffix); |
| |
| if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) |
| return SLBT_NESTED_ERROR(dctx); |
| |
| |
| strcpy(dot,".lib.a"); |
| |
| if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) |
| return SLBT_NESTED_ERROR(dctx); |
| |
| |
| strcpy(dot,".dll"); |
| |
| if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) |
| return SLBT_NESTED_ERROR(dctx); |
| |
| |
| strcpy(dot,".exe"); |
| |
| if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) |
| return SLBT_NESTED_ERROR(dctx); |
| |
| |
| *dot = 0; |
| |
| if (slbt_exec_uninstall_fs_entry(dctx,ectx,parg,path,flags)) |
| return SLBT_NESTED_ERROR(dctx); |
| |
| return 0; |
| } |
| |
| int slbt_exec_uninstall( |
| const struct slbt_driver_ctx * dctx, |
| struct slbt_exec_ctx * ectx) |
| { |
| int ret; |
| int fdout; |
| char ** argv; |
| char ** iargv; |
| uint32_t flags; |
| struct slbt_exec_ctx * actx; |
| struct argv_meta * meta; |
| struct argv_entry * entry; |
| const struct argv_option * optv[SLBT_OPTV_ELEMENTS]; |
| |
| |
| if (dctx->cctx->drvflags & SLBT_DRIVER_DRY_RUN) |
| return 0; |
| |
| |
| if (ectx) |
| actx = 0; |
| else if ((ret = slbt_get_exec_ctx(dctx,&ectx))) |
| return ret; |
| else |
| actx = ectx; |
| |
| |
| slbt_reset_arguments(ectx); |
| slbt_disable_placeholders(ectx); |
| iargv = ectx->cargv; |
| fdout = slbt_driver_fdout(dctx); |
| |
| |
| argv_optv_init(slbt_uninstall_options,optv); |
| |
| if (!iargv[1] && (dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_USAGE)) |
| return slbt_uninstall_usage( |
| fdout, |
| dctx->program, |
| 0,optv,0, |
| dctx->cctx->drvflags & SLBT_DRIVER_ANNOTATE_NEVER); |
| |
| |
| if (!(meta = argv_get( |
| iargv,optv, |
| dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_ERRORS |
| ? ARGV_VERBOSITY_ERRORS |
| : ARGV_VERBOSITY_NONE, |
| fdout))) |
| return slbt_exec_uninstall_fail( |
| actx,meta, |
| SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_UNINSTALL_FAIL)); |
| |
| |
| argv = ectx->altv; |
| flags = 0; |
| |
| *argv++ = iargv[0]; |
| |
| for (entry=meta->entries; entry->fopt || entry->arg; entry++) { |
| if (entry->fopt) { |
| switch (entry->tag) { |
| case TAG_UNINSTALL_HELP: |
| flags |= SLBT_UNINSTALL_HELP; |
| break; |
| |
| case TAG_UNINSTALL_VERSION: |
| flags |= SLBT_UNINSTALL_VERSION; |
| break; |
| |
| case TAG_UNINSTALL_FORCE: |
| *argv++ = "-f"; |
| flags |= SLBT_UNINSTALL_FORCE; |
| break; |
| |
| case TAG_UNINSTALL_RMDIR: |
| *argv++ = "-d"; |
| flags |= SLBT_UNINSTALL_RMDIR; |
| break; |
| |
| case TAG_UNINSTALL_VERBOSE: |
| *argv++ = "-v"; |
| flags |= SLBT_UNINSTALL_VERBOSE; |
| break; |
| } |
| } |
| } |
| |
| |
| if (flags & SLBT_UNINSTALL_HELP) { |
| slbt_uninstall_usage( |
| fdout, |
| dctx->program, |
| 0,optv,meta, |
| dctx->cctx->drvflags & SLBT_DRIVER_ANNOTATE_NEVER); |
| return 0; |
| } |
| |
| |
| ectx->argv = ectx->altv; |
| ectx->program = ectx->altv[0]; |
| |
| |
| for (entry=meta->entries; entry->fopt || entry->arg; entry++) |
| if (!entry->fopt) |
| if (slbt_exec_uninstall_entry(dctx,ectx,entry,argv,flags)) |
| return slbt_exec_uninstall_fail( |
| actx,meta, |
| SLBT_NESTED_ERROR(dctx)); |
| |
| argv_free(meta); |
| slbt_free_exec_ctx(actx); |
| |
| return 0; |
| } |